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_LSHIFT:
101  case SWS_UOP_RSHIFT:
102  av_bprintf(&bp, "_%u", par->shift.amount);
103  break;
104  case SWS_UOP_PERMUTE:
105  av_bprint_chars(&bp, '_', 1);
106  for (int i = 0; i < 4; i++)
107  av_bprint_chars(&bp, "xyzw"[par->swizzle.in[i]], 1);
108  break;
109  case SWS_UOP_COPY:
110  av_bprint_chars(&bp, '_', 1);
111  for (int i = 0; i < 4; i++) {
112  if (SWS_COMP_TEST(op->mask, i))
113  av_bprint_chars(&bp, "xyzw"[par->swizzle.in[i]], 1);
114  }
115  break;
116  case SWS_UOP_MOVE:
117  av_bprint_chars(&bp, '_', 1);
118  for (int i = 0; i < par->move.num_moves; i++)
119  av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1);
120  av_bprint_chars(&bp, '_', 1);
121  for (int i = 0; i < par->move.num_moves; i++)
122  av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1);
123  break;
124  case SWS_UOP_PACK:
125  case SWS_UOP_UNPACK:
126  av_bprint_chars(&bp, '_', 1);
127  for (int i = 0; i < 4 && par->pack.pattern[i]; i++)
128  av_bprintf(&bp, "%x", par->pack.pattern[i]);
129  break;
130  case SWS_UOP_CLEAR:
131  av_bprint_chars(&bp, '_', 1);
132  for (int i = 0; i < 4; i++) {
133  if (!SWS_COMP_TEST(op->mask, i))
134  continue;
135  else if (SWS_COMP_TEST(par->clear.one, i))
136  av_bprint_chars(&bp, '1', 1);
137  else if (SWS_COMP_TEST(par->clear.zero, i))
138  av_bprint_chars(&bp, '0', 1);
139  else
140  av_bprint_chars(&bp, 'x', 1);
141  }
142  break;
143  case SWS_UOP_LINEAR:
144  case SWS_UOP_LINEAR_FMA:
145  for (int i = 0; i < 4; i++) {
146  if (!SWS_COMP_TEST(op->mask, i))
147  continue;
148  av_bprint_chars(&bp, '_', 1);
149  for (int j = 0; j < 5; j++) {
150  if (par->lin.one & SWS_MASK(i, j))
151  av_bprint_chars(&bp, '1', 1);
152  else if (par->lin.zero & SWS_MASK(i, j))
153  av_bprint_chars(&bp, '0', 1);
154  else if (par->lin.exact & SWS_MASK(i, j))
155  av_bprint_chars(&bp, 'X', 1);
156  else
157  av_bprint_chars(&bp, 'x', 1);
158  }
159  }
160  break;
161  case SWS_UOP_DITHER:
162  for (int i = 0; i < 4; i++) {
163  if (SWS_COMP_TEST(op->mask, i))
164  av_bprintf(&bp, "_%d", par->dither.y_offset[i]);
165  }
166  const unsigned size = 1u << par->dither.size_log2;
167  av_bprintf(&bp, "_%ux%u", size, size);
168  break;
169  }
170 
172 }
173 
174 static void uop_uninit(SwsUOp *uop)
175 {
176  switch (uop->uop) {
177  case SWS_UOP_DITHER:
178  av_refstruct_unref(&uop->data.ptr);
179  break;
184  break;
185  }
186 
187  *uop = (SwsUOp) {0};
188 }
189 
191 {
192  SwsUOpList *ops = *p_ops;
193  if (!ops)
194  return;
195 
196  for (int i = 0; i < ops->num_ops; i++)
197  uop_uninit(&ops->ops[i]);
198 
199  av_freep(&ops->ops);
200  av_free(ops);
201  *p_ops = NULL;
202 }
203 
205 {
206  return av_mallocz(sizeof(SwsUOpList));
207 }
208 
210 {
211  if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops,
212  sizeof(*uop), (uint8_t *) uop))
213  {
214  uop_uninit(uop);
215  return AVERROR(ENOMEM);
216  }
217 
218  *uop = (SwsUOp) {0};
219  return 0;
220 }
221 
223 {
224  int max_offset = 0;
225  for (int i = 0; i < 4; i++)
226  max_offset = FFMAX(max_offset, dither->y_offset[i]);
227  return (1 << dither->size_log2) + max_offset;
228 }
229 
231 {
232  switch (ff_sws_pixel_type_size(type)) {
233  case 1: return SWS_PIXEL_U8;
234  case 2: return SWS_PIXEL_U16;
235  case 4: return SWS_PIXEL_U32;
236  default: break;
237  }
238 
239  av_unreachable("Invalid pixel type!");
240  return SWS_PIXEL_NONE;
241 }
242 
243 static bool exact_product_f32(float a, float b)
244 {
245  volatile float prod = a * b;
246  volatile float result = b ? prod / b : 0.0f;
247  return !b || result == a;
248 }
249 
251  const SwsComps *comps, int idx)
252 {
253  const AVRational64 minq = comps->min[idx];
254  const AVRational64 maxq = comps->max[idx];
256  return true;
257  else if (!minq.den || !maxq.den)
258  return false; /* unknown bounds */
259 
260  const SwsPixel min = pixel_from_q64(type, minq);
261  const SwsPixel max = pixel_from_q64(type, maxq);
262  switch (type) {
263  case SWS_PIXEL_F32:
264  return exact_product_f32(coef.f32, min.f32) &&
265  exact_product_f32(coef.f32, max.f32);
266  }
267 
268  av_unreachable("Invalid pixel type!");
269  return false;
270 }
271 
273 {
274  if (!(flags & SWS_UOP_FLAG_FMA))
275  return false;
276  if (!(ctx->flags & SWS_BITEXACT))
277  return true;
278  if (!ff_sws_pixel_type_is_int(op->type))
279  return false;
280 
281  const int bits = ff_sws_pixel_type_size(op->type) * 8;
282  const uint64_t max_val = UINT64_MAX >> (64 - bits);
283 
284  /* Maximum value representable losslessly as float. Note that this is
285  * currently true only for U8, but that may change if we ever update the
286  * value of SWS_FILTER_SCALE. */
287  return max_val * SWS_FILTER_SCALE <= (1 << 22);
288 }
289 
291  const SwsOp *op)
292 {
293  SwsUOp uop = {
294  .type = op->type,
295  .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1,
296  op->rw.elems > 2, op->rw.elems > 3),
297  };
298 
299  /* Non-filtered reads don't care about the exact pixel contents */
300  if (!op->rw.filter.op)
301  uop.type = pixel_type_to_int(op->type);
302 
303  const bool is_read = op->op == SWS_OP_READ;
304  if (op->rw.filter.op) {
305  if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR)
306  return AVERROR(ENOTSUP);
307  uop.par.filter.type = op->rw.filter.type;
308  uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel);
309  if (op->rw.filter.op == SWS_OP_FILTER_H) {
311  } else if (check_filter_fma(ctx, flags, op)) {
313  } else {
315  }
316  } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) {
317  if (op->rw.frac)
318  return AVERROR(ENOTSUP);
319  uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED;
320  } else if (op->rw.mode == SWS_RW_PALETTE) {
321  if (op->rw.frac || !is_read)
322  return AVERROR(ENOTSUP);
324  } else if (op->rw.frac == 3) {
325  uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT;
326  } else if (op->rw.frac == 1) {
327  uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE;
328  } else {
329  av_assert0(!op->rw.frac);
330  uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR;
331  }
332 
333  return ff_sws_uop_list_append(ops, &uop);
334 }
335 
336 static int count_idx(const int *arr, size_t size, int val)
337 {
338  int num = 0;
339  for (size_t i = 0; i < size; i++) {
340  if (arr[i] == val)
341  num++;
342  }
343 
344  return num;
345 }
346 
347 static int translate_move(SwsUOpList *ops, const SwsOp *op)
348 {
349  SwsUOp uop = {
350  .uop = SWS_UOP_MOVE,
351  .type = pixel_type_to_int(op->type),
352  };
353  SwsMoveUOp *par = &uop.par.move;
354 
355  /* Mask of components that are not yet satisfied */
357  for (int i = 0; i < 4; i++) {
358  if (op->swizzle.in[i] == i)
359  todo &= ~SWS_COMP(i);
360  }
361 
362  /* Mask of components whose value is required for the final output */
363  SwsCompMask needed = 0;
364  for (int i = 0; i < 4; i++) {
365  if (SWS_OP_NEEDED(op, i))
366  needed |= SWS_COMP(op->swizzle.in[i]);
367  }
368 
369  /* Current mapping of registers to components */
370  int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */
371 
372  /* Decompose the swizzle mask into a series of register-register moves */
373  while (todo) {
374  int dst = -1, src = -1;
375 
376  /* Find next unsatisfied dst <- src move that doesn't clobber a value */
377  for (dst = 0; dst < 4; dst++) {
378  if (!SWS_COMP_TEST(todo, dst))
379  continue; /* already satisfied */
380  const int cur = idx[dst];
381  if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur))
382  continue; /* clobbers last remaining, still-needed value */
383  for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) {
384  if (idx[src] == op->swizzle.in[dst]) {
385  /* Prevent read-after-write dependency. */
386  if (par->num_moves > 0 && src == par->dst[par->num_moves - 1])
387  src = par->src[par->num_moves - 1];
388  break;
389  }
390  }
391  av_assert1(src < FF_ARRAY_ELEMS(idx));
392  todo &= ~SWS_COMP(dst);
393  break;
394  }
395 
396  if (dst == 4) {
397  /* Stuck in a cycle, break it by saving to the scratch register */
398  dst = 4;
399  for (src = 0; src < 4; src++) {
400  if (SWS_COMP_TEST(todo, src)) {
401  needed &= ~SWS_COMP(idx[src]);
402  break;
403  }
404  }
405  av_assert1(src < 4);
406  }
407 
409  par->dst[par->num_moves] = dst > 3 ? -1 : dst;
410  par->src[par->num_moves] = src > 3 ? -1 : src;
411  par->num_moves++;
412  idx[dst] = idx[src];
413  }
414 
415  return ff_sws_uop_list_append(ops, &uop);
416 }
417 
419 {
420  if (flags & SWS_UOP_FLAG_MOVE)
421  return translate_move(ops, op);
422 
423  SwsUOp uop = {
424  .type = pixel_type_to_int(op->type),
425  .uop = SWS_UOP_PERMUTE,
426  .par.swizzle.in = {0, 1, 2, 3},
427  };
428 
430  SwsCompMask seen = 0;
431  for (int i = 0; i < 4; i++) {
432  if (!SWS_COMP_TEST(needed, i))
433  continue;
434  const int src = op->swizzle.in[i];
435  if (SWS_COMP_TEST(seen, src))
436  uop.uop = SWS_UOP_COPY; /* Swizzle mask contains duplicates */
437  seen |= SWS_COMP(src);
438  uop.par.swizzle.in[i] = src;
439  }
440 
441  if (uop.uop == SWS_UOP_PERMUTE) {
442  /* Prevent overlap by moving unused components to unseen indices */
443  for (int i = 0; i < 4; i++) {
444  if (SWS_COMP_TEST(needed, i))
445  continue;
446 
447  /* Prefer identity mapping if possible */
448  int unused = i;
449  if (SWS_COMP_TEST(seen, i)) {
450  for (int j = 0; j < 4; j++) {
451  if (!SWS_COMP_TEST(seen, j)) {
452  unused = j;
453  break;
454  }
455  }
456  }
457 
458  uop.par.swizzle.in[i] = unused;
459  seen |= SWS_COMP(unused);
460  }
461  }
462 
463  if (uop.uop == SWS_UOP_COPY) {
464  /* Remove remaining trivial / identity components from the mask */
465  for (int i = 0; i < 4; i++) {
466  if (uop.par.swizzle.in[i] == i)
467  needed &= ~SWS_COMP(i);
468  }
469 
470  uop.mask = needed;
471  }
472 
473  return ff_sws_uop_list_append(ops, &uop);
474 }
475 
476 static int translate_dither_op(SwsUOpList *ops, const SwsOp *op)
477 {
478  SwsUOp uop = {
479  .type = op->type,
480  .uop = SWS_UOP_DITHER,
481  .par.dither.size_log2 = op->dither.size_log2,
482  };
483 
484  if (op->dither.size_log2 == 0) {
485  /* Constant offset */
486  const SwsPixel val = Q2PIXEL(op->dither.matrix[0]);
487  uop.uop = SWS_UOP_ADD;
488  for (int i = 0; i < 4; i++) {
489  if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
490  continue;
491  uop.mask |= SWS_COMP(i);
492  uop.data.vec4[i] = val;
493  }
494 
495  return ff_sws_uop_list_append(ops, &uop);
496  }
497 
498  const int size = 1 << op->dither.size_log2;
499  for (int i = 0; i < 4; i++) {
500  if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
501  continue;
502  const uint8_t off = op->dither.y_offset[i] & (size - 1);
503  uop.mask |= SWS_COMP(i);
504  uop.par.dither.y_offset[i] = off;
505  }
506 
507  /* Allocate extra rows to allow over-reading for row offsets. Note that
508  * y_offset is currently never larger than 5, so the extra space needed
509  * for this over-allocation is bounded by 5 * size * sizeof(float),
510  * typically 320 bytes for a 16x16 dither matrix. */
511  const int stride = size * sizeof(SwsPixel);
512  const int num_rows = ff_sws_dither_height(&uop.par.dither);
513  SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride);
514  if (!matrix)
515  return AVERROR(ENOMEM);
516 
517  for (int i = 0; i < size * size; i++)
518  matrix[i] = Q2PIXEL(op->dither.matrix[i]);
519  memcpy(&matrix[size * size], matrix, (num_rows - size) * stride);
520 
521  return ff_sws_uop_list_append(ops, &uop);
522 }
523 
525  SwsUOpFlags flags, const SwsOp *op,
526  const SwsComps *input)
527 {
528  SwsUOp uop = {
529  .type = op->type,
530  .uop = SWS_UOP_LINEAR,
531  };
532 
533  const bool bitexact = ctx->flags & SWS_BITEXACT;
534  uint32_t exact = 0;
535 
536  for (int i = 0; i < 4; i++) {
537  if (SWS_OP_NEEDED(op, i) && (op->lin.mask & SWS_MASK_ROW(i)))
538  uop.mask |= SWS_COMP(i);
539  bool nonzero = (op->lin.m[i][4].num != 0);
540  for (int j = 0; j < 5; j++) {
541  const AVRational64 k = op->lin.m[i][j];
542  const SwsPixel px = Q2PIXEL(k);
543  uop.data.mat4[i][j] = px;
544  if (k.num == 0)
545  uop.par.lin.zero |= SWS_MASK(i, j);
546  else if (j < 4 && k.num == k.den)
547  uop.par.lin.one |= SWS_MASK(i, j);
548  else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j)))
549  exact |= SWS_MASK(i, j);
550  if (k.num != 0)
551  nonzero = true;
552  }
553  }
554 
555  if (flags & SWS_UOP_FLAG_FMA) {
556  /* multiplication by 1 and 0 are always exact by definition */
557  uop.uop = SWS_UOP_LINEAR_FMA;
558  uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one;
559  }
560 
561  return ff_sws_uop_list_append(ops, &uop);
562 }
563 
565 {
566  if (factor.den != 1)
567  return false;
568 
569  switch (type) {
570  case SWS_PIXEL_U8: return factor.num == UINT8_MAX;
571  case SWS_PIXEL_U16: return factor.num == UINT16_MAX;
572  case SWS_PIXEL_U32: return factor.num == UINT32_MAX;
573  case SWS_PIXEL_F32: return false;
574  case SWS_PIXEL_NONE:
575  case SWS_PIXEL_TYPE_NB: break;
576  }
577 
578  av_unreachable("Invalid pixel type!");
579  return false;
580 }
581 
583  const SwsOp *op, const SwsComps *input)
584 {
585  switch (op->op) {
586  case SWS_OP_FILTER_H:
587  case SWS_OP_FILTER_V:
588  return AVERROR(ENOTSUP); /* always handled by subpass splitting */
589  case SWS_OP_READ:
590  case SWS_OP_WRITE:
591  return translate_rw_op(ctx, uops, flags, op);
592  case SWS_OP_SWIZZLE:
593  return translate_swizzle(uops, flags, op);
594  case SWS_OP_DITHER:
595  return translate_dither_op(uops, op);
596  case SWS_OP_LINEAR:
597  return translate_linear_op(ctx, uops, flags, op, input);
598  default:
599  break;
600  }
601 
602  /* Default handling for "simple" ops */
603  SwsUOp uop = {
604  .type = op->type,
605  .uop = SWS_UOP_INVALID,
606  .mask = ff_sws_comp_mask_needed(op),
607  };
608 
609  switch (op->op) {
610  case SWS_OP_CONVERT:
611  if (op->convert.expand) {
612  av_assert0(op->type == SWS_PIXEL_U8);
613  switch (op->convert.to) {
614  case SWS_PIXEL_U16: uop.uop = SWS_UOP_EXPAND_PAIR; break;
615  case SWS_PIXEL_U32: uop.uop = SWS_UOP_EXPAND_QUAD; break;
616  }
617  } else {
618  switch (op->convert.to) {
619  case SWS_PIXEL_U8: uop.uop = SWS_UOP_TO_U8; break;
620  case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break;
621  case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break;
622  case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break;
623  }
624  }
625  break;
626  case SWS_OP_UNPACK:
627  case SWS_OP_PACK:
628  uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK;
629  uop.mask = 0;
630  for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
631  uop.par.pack.pattern[i] = op->pack.pattern[i];
632  uop.mask |= SWS_COMP(i);
633  }
634  break;
635  case SWS_OP_LSHIFT:
636  case SWS_OP_RSHIFT:
638  uop.par.shift.amount = op->shift.amount;
639  break;
640  case SWS_OP_CLEAR:
641  uop.uop = SWS_UOP_CLEAR;
642  uop.type = pixel_type_to_int(op->type);
643  uop.mask &= op->clear.mask;
644  for (int i = 0; i < 4; i++) {
645  if (!SWS_COMP_TEST(op->clear.mask, i))
646  continue;
647  const AVRational64 v = op->clear.value[i];
648  const SwsPixel px = Q2PIXEL(op->clear.value[i]);
649  uop.data.vec4[i] = px;
650  if (v.num == 0)
651  uop.par.clear.zero |= SWS_COMP(i);
652  else if (pixel_is_1s(op->type, px))
653  uop.par.clear.one |= SWS_COMP(i);
654  }
655  break;
656  case SWS_OP_SCALE:
657  if (is_expand_bit(op->type, op->scale.factor)) {
658  uop.uop = SWS_UOP_EXPAND_BIT;
659  } else {
660  uop.uop = SWS_UOP_SCALE;
661  uop.data.scalar = Q2PIXEL(op->scale.factor);
662  }
663  break;
664  case SWS_OP_MIN:
665  case SWS_OP_MAX:
666  uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX;
667  uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit);
668  for (int i = 0; i < 4; i++) {
669  if (SWS_COMP_TEST(uop.mask, i))
670  uop.data.vec4[i] = Q2PIXEL(op->clamp.limit[i]);
671  }
672  break;
673  case SWS_OP_SWAP_BYTES:
674  uop.uop = SWS_UOP_SWAP_BYTES;
675  uop.type = pixel_type_to_int(op->type);
676  break;
677  default:
678  return AVERROR(ENOTSUP);
679  }
680 
682  return ff_sws_uop_list_append(uops, &uop);
683 }
684 
687 {
688  SwsComps input = ops->comps_src;
689  for (int i = 0; i < ops->num_ops; i++) {
690  int ret = translate_op(ctx, uops, flags, &ops->ops[i], &input);
691  if (ret < 0)
692  return ret;
693  input = ops->ops[i].comps;
694  }
695  return 0;
696 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
flags
const SwsFlags flags[]
Definition: swscale.c:85
factor
static const int factor[16]
Definition: vf_pp7.c:98
SWS_UOP_SCALE
@ SWS_UOP_SCALE
Definition: uops.h:135
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:214
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:47
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:45
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
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:144
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:182
SwsUOp::data
union SwsUOp::@589 data
matrix
Definition: vc1dsp.c:43
ff_sws_comp_mask_q4
SwsCompMask ff_sws_comp_mask_q4(const AVRational64 q[4])
Definition: ops.c:137
ops.h
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:180
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:343
b
#define b
Definition: input.c:43
SWS_UOP_MOVE_MAX
#define SWS_UOP_MOVE_MAX
Definition: uops.h:168
SWS_UOP_LINEAR_FMA
@ SWS_UOP_LINEAR_FMA
Definition: uops.h:147
SWS_UOP_MAX
@ SWS_UOP_MAX
Definition: uops.h:138
translate_rw_op
static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
Definition: uops.c:290
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:74
SwsUOpParams::swizzle
SwsSwizzleUOp swizzle
Definition: uops.h:213
SWS_UOP_LSHIFT
@ SWS_UOP_LSHIFT
Definition: uops.h:143
SwsLinearUOp::one
uint32_t one
Definition: uops.h:186
SWS_UOP_TYPE_NB
@ SWS_UOP_TYPE_NB
Definition: uops.h:151
SWS_UOP_NAME_MAX
#define SWS_UOP_NAME_MAX
Generate a unique name for a SwsUOp.
Definition: uops.h:252
ff_sws_pixel_type_size
int ff_sws_pixel_type_size(SwsPixelType type)
Definition: ops.c:77
check_filter_fma
static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op)
Definition: uops.c:272
ff_sws_comp_mask_needed
SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op)
Definition: ops.c:160
SwsMoveUOp::num_moves
int num_moves
Definition: uops.h:169
SwsMoveUOp
Definition: uops.h:166
SWS_COMP_TEST
#define SWS_COMP_TEST(mask, X)
Definition: uops.h:71
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:130
SwsOpList::num_ops
int num_ops
Definition: ops.h:267
SWS_UOP_PACK
@ SWS_UOP_PACK
Definition: uops.h:142
SwsShiftUOp::amount
uint8_t amount
Definition: uops.h:159
SWS_UOP_PERMUTE
@ SWS_UOP_PERMUTE
Definition: uops.h:120
SwsUOpParams::pack
SwsPackUOp pack
Definition: uops.h:215
SWS_UOP_EXPAND_BIT
@ SWS_UOP_EXPAND_BIT
Definition: uops.h:126
translate_move
static int translate_move(SwsUOpList *ops, const SwsOp *op)
Definition: uops.c:347
UOP_NAME
#define UOP_NAME(OP, ABBR)
ff_sws_pixel_type_is_int
bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: ops.c:92
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:210
SwsFilterUOp::type
SwsPixelType type
Definition: uops.h:155
refstruct.h
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:121
SWS_UOP_INVALID
@ SWS_UOP_INVALID
Definition: uops.h:102
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:116
SWS_UOP_READ_PALETTE
@ SWS_UOP_READ_PALETTE
Definition: uops.h:112
SWS_OP_NEEDED
#define SWS_OP_NEEDED(op, idx)
Definition: ops.h:237
SwsUOp::kernel
SwsFilterWeights * kernel
Definition: uops.h:230
float
float
Definition: af_crystalizer.c:122
SWS_UOP_MOVE
@ SWS_UOP_MOVE
Definition: uops.h:122
dither
static const uint16_t dither[8][8]
Definition: vf_gradfun.c:46
SwsUOp::uop
SwsUOpType uop
Definition: uops.h:224
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:1465
SWS_UOP_WRITE_PLANAR
@ SWS_UOP_WRITE_PLANAR
Definition: uops.h:114
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:132
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:137
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:61
SWS_UOP_READ_PACKED
@ SWS_UOP_READ_PACKED
Definition: uops.h:109
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:336
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:62
SwsPixel::f32
float f32
Definition: uops.h:57
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
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:234
SWS_PIXEL_TYPE_NB
@ SWS_PIXEL_TYPE_NB
Definition: uops.h:44
SwsUOpParams::shift
SwsShiftUOp shift
Definition: uops.h:212
translate_swizzle
static int translate_swizzle(SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
Definition: uops.c:418
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsMoveUOp::dst
int8_t dst[SWS_UOP_MOVE_MAX]
Definition: uops.h:172
SwsClearUOp::one
SwsCompMask one
Definition: uops.h:181
SWS_UOP_FLAG_MOVE
@ SWS_UOP_FLAG_MOVE
Definition: uops.h:98
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:63
SWS_UOP_READ_NIBBLE
@ SWS_UOP_READ_NIBBLE
Definition: uops.h:110
SWS_UOP_ADD
@ SWS_UOP_ADD
Definition: uops.h:136
translate_dither_op
static int translate_dither_op(SwsUOpList *ops, const SwsOp *op)
Definition: uops.c:476
SwsPixelType
SwsPixelType
Definition: uops.h:38
pixel_is_1s
static bool pixel_is_1s(SwsPixelType type, SwsPixel val)
Definition: uops.c:68
SwsUOp::par
SwsUOpParams par
Definition: uops.h:226
SWS_UOP_TO_U32
@ SWS_UOP_TO_U32
Definition: uops.h:131
exact_prod
static bool exact_prod(SwsPixelType type, SwsPixel coef, const SwsComps *comps, int idx)
Definition: uops.c:250
SwsUOp
Definition: uops.h:221
SWS_UOP_WRITE_BIT
@ SWS_UOP_WRITE_BIT
Definition: uops.h:117
uop_uninit
static void uop_uninit(SwsUOp *uop)
Definition: uops.c:174
SWS_UOP_READ_PLANAR_FV_FMA
@ SWS_UOP_READ_PLANAR_FV_FMA
Definition: uops.h:108
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:187
SwsUOp::mask
SwsCompMask mask
Definition: uops.h:225
SwsDitherUOp::size_log2
uint8_t size_log2
Definition: uops.h:201
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:141
SWS_COMP
#define SWS_COMP(X)
Definition: uops.h:70
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
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:195
SwsPixel
Definition: uops.h:51
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:204
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
SWS_UOP_TO_U8
@ SWS_UOP_TO_U8
Definition: uops.h:129
exact_product_f32
static bool exact_product_f32(float a, float b)
Definition: uops.c:243
SWS_UOP_READ_PLANAR
@ SWS_UOP_READ_PLANAR
Definition: uops.h:105
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:125
SwsUOp::scalar
SwsPixel scalar
Definition: uops.h:232
SWS_UOP_LINEAR
@ SWS_UOP_LINEAR
Definition: uops.h:146
SwsOp
Definition: ops.h:210
SwsUOpParams::lin
SwsLinearUOp lin
Definition: uops.h:217
SwsPackUOp::pattern
uint8_t pattern[4]
Definition: uops.h:177
abbr
char abbr[32]
Definition: uops.c:43
SwsUOp::type
SwsPixelType type
Definition: uops.h:223
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:230
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:685
ret
ret
Definition: filter_design.txt:187
is_expand_bit
static bool is_expand_bit(SwsPixelType type, AVRational64 factor)
Definition: uops.c:564
SwsComps::min
AVRational64 min[4]
Definition: ops.h:87
SwsUOpList::num_ops
int num_ops
Definition: uops.h:257
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:190
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:122
uop_names
static const struct @587 uop_names[SWS_UOP_TYPE_NB]
SWS_RW_PALETTE
@ SWS_RW_PALETTE
Definition: ops.h:102
SwsUOp::ptr
SwsPixel * ptr
Definition: uops.h:231
SwsComps
Definition: ops.h:82
ff_sws_pixel_type_name
const char * ff_sws_pixel_type_name(SwsPixelType type)
Definition: ops.c:62
SwsLinearUOp::exact
uint32_t exact
Definition: uops.h:190
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:200
px
#define px
Definition: uops_tmpl.c:54
SwsUOpList
Definition: uops.h:255
SwsUOp::vec4
SwsPixel vec4[4]
Definition: uops.h:233
ff_sws_uop_list_append
int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
Definition: uops.c:209
SWS_UOP_DITHER
@ SWS_UOP_DITHER
Definition: uops.h:148
SWS_UOP_WRITE_PACKED
@ SWS_UOP_WRITE_PACKED
Definition: uops.h:115
SwsDitherUOp
Definition: uops.h:199
SwsUOpParams::dither
SwsDitherUOp dither
Definition: uops.h:218
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:107
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
uops.h
SWS_UOP_EXPAND_QUAD
@ SWS_UOP_EXPAND_QUAD
Definition: uops.h:128
SwsUOpFlags
uint32_t SwsUOpFlags
Definition: uops.h:94
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:106
SwsMoveUOp::src
int8_t src[SWS_UOP_MOVE_MAX]
Definition: uops.h:173
SwsUOpParams::filter
SwsFilterUOp filter
Definition: uops.h:211
translate_linear_op
static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op, const SwsComps *input)
Definition: uops.c:524
SWS_UOP_FLAG_FMA
@ SWS_UOP_FLAG_FMA
Definition: uops.h:97
ff_sws_dither_height
int ff_sws_dither_height(const SwsDitherUOp *dither)
Computes (1 << size_log2) + MAX(y_offset).
Definition: uops.c:222
translate_op
static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags, const SwsOp *op, const SwsComps *input)
Definition: uops.c:582
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:111
stride
#define stride
Definition: h264pred_template.c:536
SWS_UOP_CLEAR
@ SWS_UOP_CLEAR
Definition: uops.h:145
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
SwsContext
Main external API structure.
Definition: swscale.h:229
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: uops.h:41
SWS_MASK
#define SWS_MASK(I, J)
Definition: uops.h:193
SwsSwizzleUOp::in
uint8_t in[4]
Definition: uops.h:163
SwsUOpParams::clear
SwsClearUOp clear
Definition: uops.h:216
SwsUOpList::ops
SwsUOp * ops
Definition: uops.h:256
src
#define src
Definition: vp8dsp.c:248
SWS_UOP_EXPAND_PAIR
@ SWS_UOP_EXPAND_PAIR
Definition: uops.h:127
pixel_from_q64
static SwsPixel pixel_from_q64(SwsPixelType type, AVRational64 val)
Definition: uops.c:50
ff_sws_comp_mask_str
#define ff_sws_comp_mask_str(mask)
Definition: uops.h:82
min
float min
Definition: vorbis_enc_data.h:429