FFmpeg
ops.c
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2025 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 "libavutil/avassert.h"
22 #include "libavutil/bswap.h"
23 #include "libavutil/mem.h"
24 #include "libavutil/rational.h"
25 #include "libavutil/refstruct.h"
26 
27 #include "ops.h"
28 #include "ops_internal.h"
29 
30 extern const SwsOpBackend backend_c;
31 extern const SwsOpBackend backend_murder;
32 extern const SwsOpBackend backend_x86;
33 
34 const SwsOpBackend * const ff_sws_op_backends[] = {
36 #if ARCH_X86_64 && HAVE_X86ASM
37  &backend_x86,
38 #endif
39  &backend_c,
40  NULL
41 };
42 
43 #define RET(x) \
44  do { \
45  if ((ret = (x)) < 0) \
46  return ret; \
47  } while (0)
48 
50 {
51  switch (type) {
52  case SWS_PIXEL_U8: return "u8";
53  case SWS_PIXEL_U16: return "u16";
54  case SWS_PIXEL_U32: return "u32";
55  case SWS_PIXEL_F32: return "f32";
56  case SWS_PIXEL_NONE: return "none";
57  case SWS_PIXEL_TYPE_NB: break;
58  }
59 
60  av_unreachable("Invalid pixel type!");
61  return "ERR";
62 }
63 
65 {
66  switch (type) {
67  case SWS_PIXEL_U8: return sizeof(uint8_t);
68  case SWS_PIXEL_U16: return sizeof(uint16_t);
69  case SWS_PIXEL_U32: return sizeof(uint32_t);
70  case SWS_PIXEL_F32: return sizeof(float);
71  case SWS_PIXEL_NONE: break;
72  case SWS_PIXEL_TYPE_NB: break;
73  }
74 
75  av_unreachable("Invalid pixel type!");
76  return 0;
77 }
78 
80 {
81  switch (type) {
82  case SWS_PIXEL_U8:
83  case SWS_PIXEL_U16:
84  case SWS_PIXEL_U32:
85  return true;
86  case SWS_PIXEL_F32:
87  return false;
88  case SWS_PIXEL_NONE:
89  case SWS_PIXEL_TYPE_NB: break;
90  }
91 
92  av_unreachable("Invalid pixel type!");
93  return false;
94 }
95 
96 /* biased towards `a` */
98 {
99  return av_cmp_q(a, b) == 1 ? b : a;
100 }
101 
103 {
104  return av_cmp_q(a, b) == -1 ? b : a;
105 }
106 
108 {
109  uint64_t mask[4];
110  int shift[4];
111 
112  switch (op->op) {
113  case SWS_OP_READ:
114  case SWS_OP_WRITE:
115  return;
116  case SWS_OP_UNPACK: {
119  unsigned val = x[0].num;
120  for (int i = 0; i < 4; i++)
121  x[i] = Q((val >> shift[i]) & mask[i]);
122  return;
123  }
124  case SWS_OP_PACK: {
127  unsigned val = 0;
128  for (int i = 0; i < 4; i++)
129  val |= (x[i].num & mask[i]) << shift[i];
130  x[0] = Q(val);
131  return;
132  }
133  case SWS_OP_SWAP_BYTES:
135  switch (ff_sws_pixel_type_size(op->type)) {
136  case 2:
137  for (int i = 0; i < 4; i++)
138  x[i].num = av_bswap16(x[i].num);
139  break;
140  case 4:
141  for (int i = 0; i < 4; i++)
142  x[i].num = av_bswap32(x[i].num);
143  break;
144  }
145  return;
146  case SWS_OP_CLEAR:
147  for (int i = 0; i < 4; i++) {
148  if (op->c.q4[i].den)
149  x[i] = op->c.q4[i];
150  }
151  return;
152  case SWS_OP_LSHIFT: {
154  AVRational mult = Q(1 << op->c.u);
155  for (int i = 0; i < 4; i++)
156  x[i] = x[i].den ? av_mul_q(x[i], mult) : x[i];
157  return;
158  }
159  case SWS_OP_RSHIFT: {
161  for (int i = 0; i < 4; i++)
162  x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->c.u) : x[i];
163  return;
164  }
165  case SWS_OP_SWIZZLE: {
166  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
167  for (int i = 0; i < 4; i++)
168  x[i] = orig[op->swizzle.in[i]];
169  return;
170  }
171  case SWS_OP_CONVERT:
172  if (ff_sws_pixel_type_is_int(op->convert.to)) {
173  const AVRational scale = ff_sws_pixel_expand(op->type, op->convert.to);
174  for (int i = 0; i < 4; i++) {
175  x[i] = x[i].den ? Q(x[i].num / x[i].den) : x[i];
176  if (op->convert.expand)
177  x[i] = av_mul_q(x[i], scale);
178  }
179  }
180  return;
181  case SWS_OP_DITHER:
183  for (int i = 0; i < 4; i++)
184  x[i] = x[i].den ? av_add_q(x[i], av_make_q(1, 2)) : x[i];
185  return;
186  case SWS_OP_MIN:
187  for (int i = 0; i < 4; i++)
188  x[i] = av_min_q(x[i], op->c.q4[i]);
189  return;
190  case SWS_OP_MAX:
191  for (int i = 0; i < 4; i++)
192  x[i] = av_max_q(x[i], op->c.q4[i]);
193  return;
194  case SWS_OP_LINEAR: {
196  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
197  for (int i = 0; i < 4; i++) {
198  AVRational sum = op->lin.m[i][4];
199  for (int j = 0; j < 4; j++)
200  sum = av_add_q(sum, av_mul_q(orig[j], op->lin.m[i][j]));
201  x[i] = sum;
202  }
203  return;
204  }
205  case SWS_OP_SCALE:
206  for (int i = 0; i < 4; i++)
207  x[i] = x[i].den ? av_mul_q(x[i], op->c.q) : x[i];
208  return;
209  }
210 
211  av_unreachable("Invalid operation type!");
212 }
213 
214 /* merge_comp_flags() forms a monoid with flags_identity as the null element */
215 static const unsigned flags_identity = SWS_COMP_ZERO | SWS_COMP_EXACT;
216 static unsigned merge_comp_flags(unsigned a, unsigned b)
217 {
218  const unsigned flags_or = SWS_COMP_GARBAGE;
219  const unsigned flags_and = SWS_COMP_ZERO | SWS_COMP_EXACT;
220  return ((a & b) & flags_and) | ((a | b) & flags_or);
221 }
222 
223 /* Infer + propagate known information about components */
225 {
226  SwsComps next = { .unused = {true, true, true, true} };
227  SwsComps prev = { .flags = {
229  }};
230 
231  /* Forwards pass, propagates knowledge about the incoming pixel values */
232  for (int n = 0; n < ops->num_ops; n++) {
233  SwsOp *op = &ops->ops[n];
234 
235  switch (op->op) {
236  case SWS_OP_READ:
237  case SWS_OP_LINEAR:
238  case SWS_OP_SWAP_BYTES:
239  case SWS_OP_UNPACK:
240  break; /* special cases, handled below */
241  default:
242  memcpy(op->comps.min, prev.min, sizeof(prev.min));
243  memcpy(op->comps.max, prev.max, sizeof(prev.max));
244  ff_sws_apply_op_q(op, op->comps.min);
245  ff_sws_apply_op_q(op, op->comps.max);
246  break;
247  }
248 
249  switch (op->op) {
250  case SWS_OP_READ:
251  /* Active components are preserved from the user-provided value,
252  * other components are explicitly stripped */
253  for (int i = op->rw.elems; i < 4; i++) {
254  op->comps.flags[i] = prev.flags[i];
255  op->comps.min[i] = prev.min[i];
256  op->comps.max[i] = prev.max[i];
257  }
258  break;
259  case SWS_OP_SWAP_BYTES:
260  for (int i = 0; i < 4; i++) {
261  op->comps.flags[i] = prev.flags[i] ^ SWS_COMP_SWAPPED;
262  op->comps.min[i] = prev.min[i];
263  op->comps.max[i] = prev.max[i];
264  }
265  break;
266  case SWS_OP_WRITE:
267  for (int i = 0; i < op->rw.elems; i++)
268  av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
269  /* fall through */
270  case SWS_OP_LSHIFT:
271  case SWS_OP_RSHIFT:
272  case SWS_OP_MIN:
273  case SWS_OP_MAX:
274  /* Linearly propagate flags per component */
275  for (int i = 0; i < 4; i++)
276  op->comps.flags[i] = prev.flags[i];
277  break;
278  case SWS_OP_DITHER:
279  /* Strip zero flag because of the nonzero dithering offset */
280  for (int i = 0; i < 4; i++)
281  op->comps.flags[i] = prev.flags[i] & ~SWS_COMP_ZERO;
282  break;
283  case SWS_OP_UNPACK:
284  for (int i = 0; i < 4; i++) {
285  const int pattern = op->pack.pattern[i];
286  if (pattern) {
287  av_assert1(pattern < 32);
288  op->comps.flags[i] = prev.flags[0];
289  op->comps.min[i] = Q(0);
290  op->comps.max[i] = Q((1ULL << pattern) - 1);
291  } else
292  op->comps.flags[i] = SWS_COMP_GARBAGE;
293  }
294  break;
295  case SWS_OP_PACK: {
296  unsigned flags = flags_identity;
297  for (int i = 0; i < 4; i++) {
298  if (op->pack.pattern[i])
299  flags = merge_comp_flags(flags, prev.flags[i]);
300  if (i > 0) /* clear remaining comps for sanity */
301  op->comps.flags[i] = SWS_COMP_GARBAGE;
302  }
303  op->comps.flags[0] = flags;
304  break;
305  }
306  case SWS_OP_CLEAR:
307  for (int i = 0; i < 4; i++) {
308  if (op->c.q4[i].den) {
309  if (op->c.q4[i].num == 0) {
310  op->comps.flags[i] = SWS_COMP_ZERO | SWS_COMP_EXACT;
311  } else if (op->c.q4[i].den == 1) {
312  op->comps.flags[i] = SWS_COMP_EXACT;
313  }
314  } else {
315  op->comps.flags[i] = prev.flags[i];
316  }
317  }
318  break;
319  case SWS_OP_SWIZZLE:
320  for (int i = 0; i < 4; i++)
321  op->comps.flags[i] = prev.flags[op->swizzle.in[i]];
322  break;
323  case SWS_OP_CONVERT:
324  for (int i = 0; i < 4; i++) {
325  op->comps.flags[i] = prev.flags[i];
326  if (ff_sws_pixel_type_is_int(op->convert.to))
327  op->comps.flags[i] |= SWS_COMP_EXACT;
328  }
329  break;
330  case SWS_OP_LINEAR:
331  for (int i = 0; i < 4; i++) {
332  unsigned flags = flags_identity;
333  AVRational min = Q(0), max = Q(0);
334  for (int j = 0; j < 4; j++) {
335  const AVRational k = op->lin.m[i][j];
336  AVRational mink = av_mul_q(prev.min[j], k);
337  AVRational maxk = av_mul_q(prev.max[j], k);
338  if (k.num) {
339  flags = merge_comp_flags(flags, prev.flags[j]);
340  if (k.den != 1) /* fractional coefficient */
341  flags &= ~SWS_COMP_EXACT;
342  if (k.num < 0)
343  FFSWAP(AVRational, mink, maxk);
344  min = av_add_q(min, mink);
345  max = av_add_q(max, maxk);
346  }
347  }
348  if (op->lin.m[i][4].num) { /* nonzero offset */
349  flags &= ~SWS_COMP_ZERO;
350  if (op->lin.m[i][4].den != 1) /* fractional offset */
351  flags &= ~SWS_COMP_EXACT;
352  min = av_add_q(min, op->lin.m[i][4]);
353  max = av_add_q(max, op->lin.m[i][4]);
354  }
355  op->comps.flags[i] = flags;
356  op->comps.min[i] = min;
357  op->comps.max[i] = max;
358  }
359  break;
360  case SWS_OP_SCALE:
361  for (int i = 0; i < 4; i++) {
362  op->comps.flags[i] = prev.flags[i];
363  if (op->c.q.den != 1) /* fractional scale */
364  op->comps.flags[i] &= ~SWS_COMP_EXACT;
365  if (op->c.q.num < 0)
366  FFSWAP(AVRational, op->comps.min[i], op->comps.max[i]);
367  }
368  break;
369 
370  case SWS_OP_INVALID:
371  case SWS_OP_TYPE_NB:
372  av_unreachable("Invalid operation type!");
373  }
374 
375  prev = op->comps;
376  }
377 
378  /* Backwards pass, solves for component dependencies */
379  for (int n = ops->num_ops - 1; n >= 0; n--) {
380  SwsOp *op = &ops->ops[n];
381 
382  switch (op->op) {
383  case SWS_OP_READ:
384  case SWS_OP_WRITE:
385  for (int i = 0; i < op->rw.elems; i++)
386  op->comps.unused[i] = op->op == SWS_OP_READ;
387  for (int i = op->rw.elems; i < 4; i++)
388  op->comps.unused[i] = next.unused[i];
389  break;
390  case SWS_OP_SWAP_BYTES:
391  case SWS_OP_LSHIFT:
392  case SWS_OP_RSHIFT:
393  case SWS_OP_CONVERT:
394  case SWS_OP_DITHER:
395  case SWS_OP_MIN:
396  case SWS_OP_MAX:
397  case SWS_OP_SCALE:
398  for (int i = 0; i < 4; i++)
399  op->comps.unused[i] = next.unused[i];
400  break;
401  case SWS_OP_UNPACK: {
402  bool unused = true;
403  for (int i = 0; i < 4; i++) {
404  if (op->pack.pattern[i])
405  unused &= next.unused[i];
406  op->comps.unused[i] = i > 0;
407  }
408  op->comps.unused[0] = unused;
409  break;
410  }
411  case SWS_OP_PACK:
412  for (int i = 0; i < 4; i++) {
413  if (op->pack.pattern[i])
414  op->comps.unused[i] = next.unused[0];
415  else
416  op->comps.unused[i] = true;
417  }
418  break;
419  case SWS_OP_CLEAR:
420  for (int i = 0; i < 4; i++) {
421  if (op->c.q4[i].den)
422  op->comps.unused[i] = true;
423  else
424  op->comps.unused[i] = next.unused[i];
425  }
426  break;
427  case SWS_OP_SWIZZLE: {
428  bool unused[4] = { true, true, true, true };
429  for (int i = 0; i < 4; i++)
430  unused[op->swizzle.in[i]] &= next.unused[i];
431  for (int i = 0; i < 4; i++)
432  op->comps.unused[i] = unused[i];
433  break;
434  }
435  case SWS_OP_LINEAR:
436  for (int j = 0; j < 4; j++) {
437  bool unused = true;
438  for (int i = 0; i < 4; i++) {
439  if (op->lin.m[i][j].num)
440  unused &= next.unused[i];
441  }
442  op->comps.unused[j] = unused;
443  }
444  break;
445  }
446 
447  next = op->comps;
448  }
449 }
450 
451 static void op_uninit(SwsOp *op)
452 {
453  switch (op->op) {
454  case SWS_OP_DITHER:
455  av_refstruct_unref(&op->dither.matrix);
456  break;
457  }
458 
459  *op = (SwsOp) {0};
460 }
461 
463 {
464  SwsOpList *ops = av_mallocz(sizeof(SwsOpList));
465  if (!ops)
466  return NULL;
467 
468  ff_fmt_clear(&ops->src);
469  ff_fmt_clear(&ops->dst);
470  return ops;
471 }
472 
474 {
475  SwsOpList *ops = *p_ops;
476  if (!ops)
477  return;
478 
479  for (int i = 0; i < ops->num_ops; i++)
480  op_uninit(&ops->ops[i]);
481 
482  av_freep(&ops->ops);
483  av_free(ops);
484  *p_ops = NULL;
485 }
486 
488 {
489  SwsOpList *copy = av_malloc(sizeof(*copy));
490  if (!copy)
491  return NULL;
492 
493  int num = ops->num_ops;
494  if (num)
495  num = 1 << av_ceil_log2(num);
496 
497  *copy = *ops;
498  copy->ops = av_memdup(ops->ops, num * sizeof(ops->ops[0]));
499  if (!copy->ops) {
500  av_free(copy);
501  return NULL;
502  }
503 
504  for (int i = 0; i < ops->num_ops; i++) {
505  const SwsOp *op = &ops->ops[i];
506  switch (op->op) {
507  case SWS_OP_DITHER:
508  av_refstruct_ref(copy->ops[i].dither.matrix);
509  break;
510  }
511  }
512 
513  return copy;
514 }
515 
516 void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
517 {
518  const int end = ops->num_ops - count;
519  av_assert2(index >= 0 && count >= 0 && index + count <= ops->num_ops);
520  op_uninit(&ops->ops[index]);
521  for (int i = index; i < end; i++)
522  ops->ops[i] = ops->ops[i + count];
523  ops->num_ops = end;
524 }
525 
527 {
528  void *ret = av_dynarray2_add((void **) &ops->ops, &ops->num_ops, sizeof(*op), NULL);
529  if (!ret) {
530  op_uninit(op);
531  return AVERROR(ENOMEM);
532  }
533 
534  for (int i = ops->num_ops - 1; i > index; i--)
535  ops->ops[i] = ops->ops[i - 1];
536  ops->ops[index] = *op;
537  return 0;
538 }
539 
541 {
542  return ff_sws_op_list_insert_at(ops, ops->num_ops, op);
543 }
544 
546 {
547  int max_size = 0;
548  for (int i = 0; i < ops->num_ops; i++) {
549  const int size = ff_sws_pixel_type_size(ops->ops[i].type);
550  max_size = FFMAX(max_size, size);
551  }
552 
553  return max_size;
554 }
555 
557 {
558  uint32_t mask = 0;
559  for (int i = 0; i < 4; i++) {
560  for (int j = 0; j < 5; j++) {
561  if (av_cmp_q(c.m[i][j], Q(i == j)))
562  mask |= SWS_MASK(i, j);
563  }
564  }
565  return mask;
566 }
567 
568 static const char *describe_lin_mask(uint32_t mask)
569 {
570  /* Try to be fairly descriptive without assuming too much */
571  static const struct {
572  char name[24];
573  uint32_t mask;
574  } patterns[] = {
575  { "noop", 0 },
576  { "luma", SWS_MASK_LUMA },
577  { "alpha", SWS_MASK_ALPHA },
578  { "luma+alpha", SWS_MASK_LUMA | SWS_MASK_ALPHA },
579  { "dot3", 0x7 },
580  { "dot4", 0xF },
581  { "row0", SWS_MASK_ROW(0) },
582  { "row0+alpha", SWS_MASK_ROW(0) | SWS_MASK_ALPHA },
583  { "col0", SWS_MASK_COL(0) },
584  { "col0+off3", SWS_MASK_COL(0) | SWS_MASK_OFF3 },
585  { "off3", SWS_MASK_OFF3 },
586  { "off3+alpha", SWS_MASK_OFF3 | SWS_MASK_ALPHA },
587  { "diag3", SWS_MASK_DIAG3 },
588  { "diag4", SWS_MASK_DIAG4 },
589  { "diag3+alpha", SWS_MASK_DIAG3 | SWS_MASK_ALPHA },
590  { "diag3+off3", SWS_MASK_DIAG3 | SWS_MASK_OFF3 },
591  { "diag3+off3+alpha", SWS_MASK_DIAG3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
592  { "diag4+off4", SWS_MASK_DIAG4 | SWS_MASK_OFF4 },
593  { "matrix3", SWS_MASK_MAT3 },
594  { "matrix3+off3", SWS_MASK_MAT3 | SWS_MASK_OFF3 },
595  { "matrix3+off3+alpha", SWS_MASK_MAT3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
596  { "matrix4", SWS_MASK_MAT4 },
597  { "matrix4+off4", SWS_MASK_MAT4 | SWS_MASK_OFF4 },
598  };
599 
600  for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
601  if (!(mask & ~patterns[i].mask))
602  return patterns[i].name;
603  }
604 
605  av_unreachable("Invalid linear mask!");
606  return "ERR";
607 }
608 
609 static char describe_comp_flags(unsigned flags)
610 {
611  if (flags & SWS_COMP_GARBAGE)
612  return 'X';
613  else if (flags & SWS_COMP_ZERO)
614  return '0';
615  else if (flags & SWS_COMP_SWAPPED)
616  return 'z';
617  else if (flags & SWS_COMP_EXACT)
618  return '+';
619  else
620  return '.';
621 }
622 
623 static const char *print_q(const AVRational q, char buf[], int buf_len)
624 {
625  if (!q.den) {
626  return q.num > 0 ? "inf" : q.num < 0 ? "-inf" : "nan";
627  } else if (q.den == 1) {
628  snprintf(buf, buf_len, "%d", q.num);
629  return buf;
630  } else if (abs(q.num) > 1000 || abs(q.den) > 1000) {
631  snprintf(buf, buf_len, "%f", av_q2d(q));
632  return buf;
633  } else {
634  snprintf(buf, buf_len, "%d/%d", q.num, q.den);
635  return buf;
636  }
637 }
638 
639 #define PRINTQ(q) print_q(q, (char[32]){0}, sizeof(char[32]) - 1)
640 
641 void ff_sws_op_list_print(void *log, int lev, const SwsOpList *ops)
642 {
643  if (!ops->num_ops) {
644  av_log(log, lev, " (empty)\n");
645  return;
646  }
647 
648  for (int i = 0; i < ops->num_ops; i++) {
649  const SwsOp *op = &ops->ops[i];
650  av_log(log, lev, " [%3s %c%c%c%c -> %c%c%c%c] ",
651  ff_sws_pixel_type_name(op->type),
652  op->comps.unused[0] ? 'X' : '.',
653  op->comps.unused[1] ? 'X' : '.',
654  op->comps.unused[2] ? 'X' : '.',
655  op->comps.unused[3] ? 'X' : '.',
656  describe_comp_flags(op->comps.flags[0]),
657  describe_comp_flags(op->comps.flags[1]),
658  describe_comp_flags(op->comps.flags[2]),
659  describe_comp_flags(op->comps.flags[3]));
660 
661  switch (op->op) {
662  case SWS_OP_INVALID:
663  av_log(log, lev, "SWS_OP_INVALID\n");
664  break;
665  case SWS_OP_READ:
666  case SWS_OP_WRITE:
667  av_log(log, lev, "%-20s: %d elem(s) %s >> %d\n",
668  op->op == SWS_OP_READ ? "SWS_OP_READ"
669  : "SWS_OP_WRITE",
670  op->rw.elems, op->rw.packed ? "packed" : "planar",
671  op->rw.frac);
672  break;
673  case SWS_OP_SWAP_BYTES:
674  av_log(log, lev, "SWS_OP_SWAP_BYTES\n");
675  break;
676  case SWS_OP_LSHIFT:
677  av_log(log, lev, "%-20s: << %u\n", "SWS_OP_LSHIFT", op->c.u);
678  break;
679  case SWS_OP_RSHIFT:
680  av_log(log, lev, "%-20s: >> %u\n", "SWS_OP_RSHIFT", op->c.u);
681  break;
682  case SWS_OP_PACK:
683  case SWS_OP_UNPACK:
684  av_log(log, lev, "%-20s: {%d %d %d %d}\n",
685  op->op == SWS_OP_PACK ? "SWS_OP_PACK"
686  : "SWS_OP_UNPACK",
687  op->pack.pattern[0], op->pack.pattern[1],
688  op->pack.pattern[2], op->pack.pattern[3]);
689  break;
690  case SWS_OP_CLEAR:
691  av_log(log, lev, "%-20s: {%s %s %s %s}\n", "SWS_OP_CLEAR",
692  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
693  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
694  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
695  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
696  break;
697  case SWS_OP_SWIZZLE:
698  av_log(log, lev, "%-20s: %d%d%d%d\n", "SWS_OP_SWIZZLE",
699  op->swizzle.x, op->swizzle.y, op->swizzle.z, op->swizzle.w);
700  break;
701  case SWS_OP_CONVERT:
702  av_log(log, lev, "%-20s: %s -> %s%s\n", "SWS_OP_CONVERT",
703  ff_sws_pixel_type_name(op->type),
704  ff_sws_pixel_type_name(op->convert.to),
705  op->convert.expand ? " (expand)" : "");
706  break;
707  case SWS_OP_DITHER:
708  av_log(log, lev, "%-20s: %dx%d matrix + {%d %d %d %d}\n", "SWS_OP_DITHER",
709  1 << op->dither.size_log2, 1 << op->dither.size_log2,
710  op->dither.y_offset[0], op->dither.y_offset[1],
711  op->dither.y_offset[2], op->dither.y_offset[3]);
712  break;
713  case SWS_OP_MIN:
714  av_log(log, lev, "%-20s: x <= {%s %s %s %s}\n", "SWS_OP_MIN",
715  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
716  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
717  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
718  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
719  break;
720  case SWS_OP_MAX:
721  av_log(log, lev, "%-20s: {%s %s %s %s} <= x\n", "SWS_OP_MAX",
722  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
723  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
724  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
725  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
726  break;
727  case SWS_OP_LINEAR:
728  av_log(log, lev, "%-20s: %s [[%s %s %s %s %s] "
729  "[%s %s %s %s %s] "
730  "[%s %s %s %s %s] "
731  "[%s %s %s %s %s]]\n",
732  "SWS_OP_LINEAR", describe_lin_mask(op->lin.mask),
733  PRINTQ(op->lin.m[0][0]), PRINTQ(op->lin.m[0][1]), PRINTQ(op->lin.m[0][2]), PRINTQ(op->lin.m[0][3]), PRINTQ(op->lin.m[0][4]),
734  PRINTQ(op->lin.m[1][0]), PRINTQ(op->lin.m[1][1]), PRINTQ(op->lin.m[1][2]), PRINTQ(op->lin.m[1][3]), PRINTQ(op->lin.m[1][4]),
735  PRINTQ(op->lin.m[2][0]), PRINTQ(op->lin.m[2][1]), PRINTQ(op->lin.m[2][2]), PRINTQ(op->lin.m[2][3]), PRINTQ(op->lin.m[2][4]),
736  PRINTQ(op->lin.m[3][0]), PRINTQ(op->lin.m[3][1]), PRINTQ(op->lin.m[3][2]), PRINTQ(op->lin.m[3][3]), PRINTQ(op->lin.m[3][4]));
737  break;
738  case SWS_OP_SCALE:
739  av_log(log, lev, "%-20s: * %s\n", "SWS_OP_SCALE",
740  PRINTQ(op->c.q));
741  break;
742  case SWS_OP_TYPE_NB:
743  break;
744  }
745 
746  if (op->comps.min[0].den || op->comps.min[1].den ||
747  op->comps.min[2].den || op->comps.min[3].den ||
748  op->comps.max[0].den || op->comps.max[1].den ||
749  op->comps.max[2].den || op->comps.max[3].den)
750  {
751  av_log(log, AV_LOG_TRACE, " min: {%s, %s, %s, %s}, max: {%s, %s, %s, %s}\n",
752  PRINTQ(op->comps.min[0]), PRINTQ(op->comps.min[1]),
753  PRINTQ(op->comps.min[2]), PRINTQ(op->comps.min[3]),
754  PRINTQ(op->comps.max[0]), PRINTQ(op->comps.max[1]),
755  PRINTQ(op->comps.max[2]), PRINTQ(op->comps.max[3]));
756  }
757 
758  }
759 
760  av_log(log, lev, " (X = unused, z = byteswapped, + = exact, 0 = zero)\n");
761 }
762 
764  const SwsOpList *ops, SwsCompiledOp *out)
765 {
766  SwsOpList *copy, rest;
767  SwsCompiledOp compiled = {0};
768  int ret = 0;
769 
771  if (!copy)
772  return AVERROR(ENOMEM);
773 
774  /* Ensure these are always set during compilation */
776 
777  /* Make an on-stack copy of `ops` to ensure we can still properly clean up
778  * the copy afterwards */
779  rest = *copy;
780 
781  ret = backend->compile(ctx, &rest, &compiled);
782  if (ret < 0) {
783  int msg_lev = ret == AVERROR(ENOTSUP) ? AV_LOG_TRACE : AV_LOG_ERROR;
784  av_log(ctx, msg_lev, "Backend '%s' failed to compile operations: %s\n",
785  backend->name, av_err2str(ret));
786  if (rest.num_ops != ops->num_ops) {
787  av_log(ctx, msg_lev, "Uncompiled remainder:\n");
788  ff_sws_op_list_print(ctx, msg_lev, &rest);
789  }
790  } else {
791  *out = compiled;
792  }
793 
795  return ret;
796 }
797 
799 {
800  for (int n = 0; ff_sws_op_backends[n]; n++) {
801  const SwsOpBackend *backend = ff_sws_op_backends[n];
802  if (ff_sws_ops_compile_backend(ctx, backend, ops, out) < 0)
803  continue;
804 
805  av_log(ctx, AV_LOG_VERBOSE, "Compiled using backend '%s': "
806  "block size = %d, over-read = %d, over-write = %d, cpu flags = 0x%x\n",
807  backend->name, out->block_size, out->over_read, out->over_write,
808  out->cpu_flags);
809  return 0;
810  }
811 
812  av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
814  return AVERROR(ENOTSUP);
815 }
816 
817 typedef struct SwsOpPass {
819  SwsOpExec exec_base;
820  int num_blocks;
821  int tail_off_in;
822  int tail_off_out;
823  int tail_size_in;
824  int tail_size_out;
825  int planes_in;
826  int planes_out;
827  int pixel_bits_in;
828  int pixel_bits_out;
829  bool memcpy_in;
830  bool memcpy_out;
831 } SwsOpPass;
832 
833 static void op_pass_free(void *ptr)
834 {
835  SwsOpPass *p = ptr;
836  if (!p)
837  return;
838 
839  if (p->comp.free)
840  p->comp.free(p->comp.priv);
841 
842  av_free(p);
843 }
844 
845 static void op_pass_setup(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
846 {
847  const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(in->fmt);
848  const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->fmt);
849 
850  SwsOpPass *p = pass->priv;
851  SwsOpExec *exec = &p->exec_base;
852  const SwsCompiledOp *comp = &p->comp;
853  const int block_size = comp->block_size;
854  p->num_blocks = (pass->width + block_size - 1) / block_size;
855 
856  /* Set up main loop parameters */
857  const int aligned_w = p->num_blocks * block_size;
858  const int safe_width = (p->num_blocks - 1) * block_size;
859  const int tail_size = pass->width - safe_width;
860  p->tail_off_in = safe_width * p->pixel_bits_in >> 3;
861  p->tail_off_out = safe_width * p->pixel_bits_out >> 3;
862  p->tail_size_in = tail_size * p->pixel_bits_in >> 3;
863  p->tail_size_out = tail_size * p->pixel_bits_out >> 3;
864  p->memcpy_in = false;
865  p->memcpy_out = false;
866 
867  for (int i = 0; i < p->planes_in; i++) {
868  const int sub_x = (i == 1 || i == 2) ? indesc->log2_chroma_w : 0;
869  const int plane_w = (aligned_w + sub_x) >> sub_x;
870  const int plane_pad = (comp->over_read + sub_x) >> sub_x;
871  const int plane_size = plane_w * p->pixel_bits_in >> 3;
872  p->memcpy_in |= plane_size + plane_pad > in->linesize[i];
873  exec->in_stride[i] = in->linesize[i];
874  }
875 
876  for (int i = 0; i < p->planes_out; i++) {
877  const int sub_x = (i == 1 || i == 2) ? outdesc->log2_chroma_w : 0;
878  const int plane_w = (aligned_w + sub_x) >> sub_x;
879  const int plane_pad = (comp->over_write + sub_x) >> sub_x;
880  const int plane_size = plane_w * p->pixel_bits_out >> 3;
881  p->memcpy_out |= plane_size + plane_pad > out->linesize[i];
882  exec->out_stride[i] = out->linesize[i];
883  }
884 
885  /* Pre-fill pointer bump for the main section only; this value does not
886  * matter at all for the tail / last row handlers because they only ever
887  * process a single line */
888  const int blocks_main = p->num_blocks - p->memcpy_out;
889  for (int i = 0; i < 4; i++) {
890  exec->in_bump[i] = in->linesize[i] - blocks_main * exec->block_size_in;
891  exec->out_bump[i] = out->linesize[i] - blocks_main * exec->block_size_out;
892  }
893 }
894 
895 /* Dispatch kernel over the last column of the image using memcpy */
896 static av_always_inline void
897 handle_tail(const SwsOpPass *p, SwsOpExec *exec,
898  const SwsImg *out_base, const bool copy_out,
899  const SwsImg *in_base, const bool copy_in,
900  int y, const int h)
901 {
902  DECLARE_ALIGNED_64(uint8_t, tmp)[2][4][sizeof(uint32_t[128])];
903 
904  const SwsCompiledOp *comp = &p->comp;
905  const int tail_size_in = p->tail_size_in;
906  const int tail_size_out = p->tail_size_out;
907  const int bx = p->num_blocks - 1;
908 
909  SwsImg in = ff_sws_img_shift(in_base, y);
910  SwsImg out = ff_sws_img_shift(out_base, y);
911  for (int i = 0; i < p->planes_in; i++) {
912  in.data[i] += p->tail_off_in;
913  if (copy_in) {
914  exec->in[i] = (void *) tmp[0][i];
915  exec->in_stride[i] = sizeof(tmp[0][i]);
916  } else {
917  exec->in[i] = in.data[i];
918  }
919  }
920 
921  for (int i = 0; i < p->planes_out; i++) {
922  out.data[i] += p->tail_off_out;
923  if (copy_out) {
924  exec->out[i] = (void *) tmp[1][i];
925  exec->out_stride[i] = sizeof(tmp[1][i]);
926  } else {
927  exec->out[i] = out.data[i];
928  }
929  }
930 
931  for (int y_end = y + h; y < y_end; y++) {
932  if (copy_in) {
933  for (int i = 0; i < p->planes_in; i++) {
934  av_assert2(tmp[0][i] + tail_size_in < (uint8_t *) tmp[1]);
935  memcpy(tmp[0][i], in.data[i], tail_size_in);
936  in.data[i] += in.linesize[i];
937  }
938  }
939 
940  comp->func(exec, comp->priv, bx, y, p->num_blocks, y + 1);
941 
942  if (copy_out) {
943  for (int i = 0; i < p->planes_out; i++) {
944  av_assert2(tmp[1][i] + tail_size_out < (uint8_t *) tmp[2]);
945  memcpy(out.data[i], tmp[1][i], tail_size_out);
946  out.data[i] += out.linesize[i];
947  }
948  }
949 
950  for (int i = 0; i < 4; i++) {
951  if (!copy_in)
952  exec->in[i] += in.linesize[i];
953  if (!copy_out)
954  exec->out[i] += out.linesize[i];
955  }
956  }
957 }
958 
959 static void op_pass_run(const SwsImg *out_base, const SwsImg *in_base,
960  const int y, const int h, const SwsPass *pass)
961 {
962  const SwsOpPass *p = pass->priv;
963  const SwsCompiledOp *comp = &p->comp;
964  const SwsImg in = ff_sws_img_shift(in_base, y);
965  const SwsImg out = ff_sws_img_shift(out_base, y);
966 
967  /* Fill exec metadata for this slice */
968  DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
969  exec.slice_y = y;
970  exec.slice_h = h;
971  for (int i = 0; i < 4; i++) {
972  exec.in[i] = in.data[i];
973  exec.out[i] = out.data[i];
974  }
975 
976  /**
977  * To ensure safety, we need to consider the following:
978  *
979  * 1. We can overread the input, unless this is the last line of an
980  * unpadded buffer. All defined operations can handle arbitrary pixel
981  * input, so overread of arbitrary data is fine.
982  *
983  * 2. We can overwrite the output, as long as we don't write more than the
984  * amount of pixels that fit into one linesize. So we always need to
985  * memcpy the last column on the output side if unpadded.
986  *
987  * 3. For the last row, we also need to memcpy the remainder of the input,
988  * to avoid reading past the end of the buffer. Note that since we know
989  * the run() function is called on stripes of the same buffer, we don't
990  * need to worry about this for the end of a slice.
991  */
992 
993  const int last_slice = y + h == pass->height;
994  const bool memcpy_in = last_slice && p->memcpy_in;
995  const bool memcpy_out = p->memcpy_out;
996  const int num_blocks = p->num_blocks;
997  const int blocks_main = num_blocks - memcpy_out;
998  const int h_main = h - memcpy_in;
999 
1000  /* Handle main section */
1001  comp->func(&exec, comp->priv, 0, y, blocks_main, y + h_main);
1002 
1003  if (memcpy_in) {
1004  /* Safe part of last row */
1005  for (int i = 0; i < 4; i++) {
1006  exec.in[i] += h_main * in.linesize[i];
1007  exec.out[i] += h_main * out.linesize[i];
1008  }
1009  comp->func(&exec, comp->priv, 0, y + h_main, num_blocks - 1, y + h);
1010  }
1011 
1012  /* Handle last column via memcpy, takes over `exec` so call these last */
1013  if (memcpy_out)
1014  handle_tail(p, &exec, out_base, true, in_base, false, y, h_main);
1015  if (memcpy_in)
1016  handle_tail(p, &exec, out_base, memcpy_out, in_base, true, y + h_main, 1);
1017 }
1018 
1019 static int rw_planes(const SwsOp *op)
1020 {
1021  return op->rw.packed ? 1 : op->rw.elems;
1022 }
1023 
1024 static int rw_pixel_bits(const SwsOp *op)
1025 {
1026  const int elems = op->rw.packed ? op->rw.elems : 1;
1027  const int size = ff_sws_pixel_type_size(op->type);
1028  const int bits = 8 >> op->rw.frac;
1029  av_assert1(bits >= 1);
1030  return elems * size * bits;
1031 }
1032 
1033 int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags, SwsFormat dst,
1035 {
1036  SwsContext *ctx = graph->ctx;
1037  SwsOpPass *p = NULL;
1038  const SwsOp *read = &ops->ops[0];
1039  const SwsOp *write = &ops->ops[ops->num_ops - 1];
1040  SwsPass *pass;
1041  int ret;
1042 
1043  if (ops->num_ops < 2) {
1044  av_log(ctx, AV_LOG_ERROR, "Need at least two operations.\n");
1045  return AVERROR(EINVAL);
1046  }
1047 
1048  if (read->op != SWS_OP_READ || write->op != SWS_OP_WRITE) {
1049  av_log(ctx, AV_LOG_ERROR, "First and last operations must be a read "
1050  "and write, respectively.\n");
1051  return AVERROR(EINVAL);
1052  }
1053 
1056  else
1058 
1059  p = av_mallocz(sizeof(*p));
1060  if (!p)
1061  return AVERROR(ENOMEM);
1062 
1063  ret = ff_sws_ops_compile(ctx, ops, &p->comp);
1064  if (ret < 0)
1065  goto fail;
1066 
1067  p->planes_in = rw_planes(read);
1068  p->planes_out = rw_planes(write);
1069  p->pixel_bits_in = rw_pixel_bits(read);
1070  p->pixel_bits_out = rw_pixel_bits(write);
1071  p->exec_base = (SwsOpExec) {
1072  .width = dst.width,
1073  .height = dst.height,
1074  .block_size_in = p->comp.block_size * p->pixel_bits_in >> 3,
1075  .block_size_out = p->comp.block_size * p->pixel_bits_out >> 3,
1076  };
1077 
1078  pass = ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height, input,
1079  1, p, op_pass_run);
1080  if (!pass) {
1081  ret = AVERROR(ENOMEM);
1082  goto fail;
1083  }
1084  pass->setup = op_pass_setup;
1085  pass->free = op_pass_free;
1086 
1087  *output = pass;
1088  return 0;
1089 
1090 fail:
1091  op_pass_free(p);
1092  return ret;
1093 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:47
flags
const SwsFlags flags[]
Definition: swscale.c:61
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: ops.h:33
ff_sws_op_list_free
void ff_sws_op_list_free(SwsOpList **p_ops)
Definition: ops.c:473
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
name
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 default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
SwsComps::flags
unsigned flags[4]
Definition: ops.h:90
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:50
SwsGraph::ctx
SwsContext * ctx
Definition: graph.h:109
SwsPass
Represents a single filter pass in the scaling graph.
Definition: graph.h:68
ff_sws_op_list_alloc
SwsOpList * ff_sws_op_list_alloc(void)
Definition: ops.c:462
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
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:55
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:53
ff_sws_op_list_duplicate
SwsOpList * ff_sws_op_list_duplicate(const SwsOpList *ops)
Returns a duplicate of ops, or NULL on OOM.
Definition: ops.c:487
out
FILE * out
Definition: movenc.c:55
av_min_q
static AVRational av_min_q(AVRational a, AVRational b)
Definition: ops.c:97
comp
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition: eamad.c:79
SWS_MASK_DIAG3
@ SWS_MASK_DIAG3
Definition: ops.h:172
SwsOpExec::in_bump
ptrdiff_t in_bump[4]
Definition: ops_internal.h:68
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
SWS_COMP_ZERO
@ SWS_COMP_ZERO
Definition: ops.h:75
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:59
ff_sws_linear_mask
uint32_t ff_sws_linear_mask(const SwsLinearOp c)
Definition: ops.c:556
SwsOpExec::in
const uint8_t * in[4]
Definition: ops_internal.h:60
SwsOpExec::out_stride
ptrdiff_t out_stride[4]
Definition: ops_internal.h:65
ff_sws_op_list_max_size
int ff_sws_op_list_max_size(const SwsOpList *ops)
Returns the size of the largest pixel type used in ops.
Definition: ops.c:545
backend_x86
const SwsOpBackend backend_x86
Definition: ops.c:743
SwsComps::unused
bool unused[4]
Definition: ops.h:91
rational.h
output
filter_frame For filters that do not use the this method is called when a frame is pushed to the filter s input It can be called at any time except in a reentrant way If the input frame is enough to produce output
Definition: filter_design.txt:226
ff_sws_op_list_append
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
These will take over ownership of op and set it to {0}, even on failure.
Definition: ops.c:540
normalize.log
log
Definition: normalize.py:21
mask
int mask
Definition: mediacodecdec_common.c:154
SWS_MASK_OFF3
@ SWS_MASK_OFF3
Definition: ops.h:173
ops.h
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:67
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
SwsOpExec::block_size_in
int32_t block_size_in
Definition: ops_internal.h:74
b
#define b
Definition: input.c:42
SWS_MASK_LUMA
@ SWS_MASK_LUMA
Definition: ops.h:169
SWS_MASK_ALPHA
@ SWS_MASK_ALPHA
Definition: ops.h:170
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:226
max
#define max(a, b)
Definition: cuda_runtime.h:33
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: ops.h:34
SWS_OP_TYPE_NB
@ SWS_OP_TYPE_NB
Definition: ops.h:69
SwsPass::free
void(* free)(void *priv)
Optional private state and associated free() function.
Definition: graph.h:101
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
SwsOpExec::in_stride
ptrdiff_t in_stride[4]
Definition: ops_internal.h:64
SwsImg
Represents a view into a single field of frame data.
Definition: graph.h:33
SwsOpBackend::name
const char * name
Definition: ops_internal.h:106
ff_sws_pixel_type_size
int ff_sws_pixel_type_size(SwsPixelType type)
Definition: ops.c:64
describe_comp_flags
static char describe_comp_flags(unsigned flags)
Definition: ops.c:609
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: ops.h:164
av_memdup
void * av_memdup(const void *p, size_t size)
Duplicate a buffer with av_malloc().
Definition: mem.c:304
DECLARE_ALIGNED_64
#define DECLARE_ALIGNED_64(t, v)
Definition: mem_internal.h:114
SwsPixelType
SwsPixelType
Copyright (C) 2025 Niklas Haas.
Definition: ops.h:30
SwsPass::width
int width
Definition: graph.h:78
SwsComps::max
AVRational max[4]
Definition: ops.h:95
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: ops.h:35
SWS_MASK_OFF4
@ SWS_MASK_OFF4
Definition: ops.h:179
ff_sws_op_backends
const SwsOpBackend *const ff_sws_op_backends[]
Definition: ops.c:34
av_ceil_log2
#define av_ceil_log2
Definition: common.h:97
fail
#define fail()
Definition: checkasm.h:214
SwsOpList::num_ops
int num_ops
Definition: ops.h:231
SWS_MASK_COL
#define SWS_MASK_COL(J)
Definition: ops.h:165
flags_identity
static const unsigned flags_identity
Definition: ops.c:215
SwsOpBackend::compile
int(* compile)(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out)
Compile an operation list to an implementation chain.
Definition: ops_internal.h:114
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: ops.h:32
ff_sws_pixel_type_is_int
bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: ops.c:79
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
AVRational::num
int num
Numerator.
Definition: rational.h:59
ff_sws_ops_compile_backend
int ff_sws_ops_compile_backend(SwsContext *ctx, const SwsOpBackend *backend, const SwsOpList *ops, SwsCompiledOp *out)
Attempt to compile a list of operations using a specific backend.
refstruct.h
RET
#define RET(x)
Definition: ops.c:43
SwsOp::op
SwsOpType op
Definition: ops.h:187
Q
#define Q(q)
mult
static int16_t mult(Float11 *f1, Float11 *f2)
Definition: g726.c:60
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:63
avassert.h
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:236
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
SwsPass::priv
void * priv
Definition: graph.h:102
float
float
Definition: af_crystalizer.c:122
SwsComps::min
AVRational min[4]
Definition: ops.h:95
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
av_q2d
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
backend_c
const SwsOpBackend backend_c
Copyright (C) 2025 Niklas Haas.
Definition: ops_backend.c:106
SwsPass::setup
void(* setup)(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
Called once from the main thread before running the filter.
Definition: graph.h:96
bits
uint8_t bits
Definition: vp3data.h:128
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:61
ctx
AVFormatContext * ctx
Definition: movenc.c:49
ff_sws_pixel_expand
static AVRational ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
Definition: ops_internal.h:30
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:66
AVPixFmtDescriptor::log2_chroma_w
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:80
PRINTQ
#define PRINTQ(q)
Definition: ops.c:639
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
SwsOpBackend
Definition: ops_internal.h:105
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:54
SwsOpExec
Global execution context for all compiled functions.
Definition: ops_internal.h:58
merge_comp_flags
static unsigned merge_comp_flags(unsigned a, unsigned b)
Definition: ops.c:216
ff_sws_graph_add_pass
SwsPass * ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt, int width, int height, SwsPass *input, int align, void *priv, sws_filter_run_t run)
Allocate and add a new pass to the filter graph.
Definition: graph.c:48
SWS_MASK_DIAG4
@ SWS_MASK_DIAG4
Definition: ops.h:178
rw_pixel_bits
static int rw_pixel_bits(const SwsOp *op)
Definition: sw_ops.c:55
NULL
#define NULL
Definition: coverity.c:32
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:108
SWS_COMP_GARBAGE
@ SWS_COMP_GARBAGE
Definition: ops.h:73
abs
#define abs(x)
Definition: cuda_runtime.h:35
ff_sws_op_list_remove_at
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
Definition: ops.c:516
SwsOpExec::slice_h
int32_t slice_h
Definition: ops_internal.h:73
print_q
static const char * print_q(const AVRational q, char buf[], int buf_len)
Definition: ops.c:623
SWS_MASK
#define SWS_MASK(I, J)
Definition: ops.h:162
SWS_PIXEL_NONE
@ SWS_PIXEL_NONE
Definition: ops.h:31
index
int index
Definition: gxfenc.c:90
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
ff_sws_apply_op_q
void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
Apply an operation to an AVRational.
Definition: ops.c:107
SwsPass::height
int height
Definition: graph.h:78
SwsImg::linesize
int linesize[4]
Definition: graph.h:36
SwsOpExec::block_size_out
int32_t block_size_out
Definition: ops_internal.h:75
copy
static void copy(const float *p1, float *p2, const int length)
Definition: vf_vaguedenoiser.c:186
shift
static int shift(int a, int b)
Definition: bonk.c:261
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
av_bswap32
#define av_bswap32
Definition: bswap.h:47
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
SwsOp::type
SwsPixelType type
Definition: ops.h:188
ff_sws_op_list_insert_at
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
Definition: ops.c:526
size
int size
Definition: twinvq_data.h:10344
av_make_q
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition: rational.h:71
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:56
SwsOpList::src
SwsFormat src
Definition: ops.h:234
SWS_OP_INVALID
@ SWS_OP_INVALID
Definition: ops.h:44
ff_sws_op_list_update_comps
void ff_sws_op_list_update_comps(SwsOpList *ops)
Infer + propagate known information about components.
Definition: ops.c:224
SwsFormat
Definition: format.h:77
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:48
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
op_uninit
static void op_uninit(SwsOp *op)
Definition: ops.c:451
SWS_OP_FLAG_OPTIMIZE
@ SWS_OP_FLAG_OPTIMIZE
Definition: ops.h:277
SwsLinearOp
Definition: ops.h:145
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
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
SwsOpExec::out
uint8_t * out[4]
Definition: ops_internal.h:61
ff_sws_op_list_optimize
int ff_sws_op_list_optimize(SwsOpList *ops)
Fuse compatible and eliminate redundant operations, as well as replacing some operations with more ef...
Definition: ops_optimizer.c:257
av_assert2
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition: avassert.h:68
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
av_max_q
static AVRational av_max_q(AVRational a, AVRational b)
Definition: ops.c:102
SwsOpList::ops
SwsOp * ops
Definition: ops.h:230
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:57
av_always_inline
#define av_always_inline
Definition: attributes.h:63
DECLARE_ALIGNED_32
#define DECLARE_ALIGNED_32(t, v)
Definition: mem_internal.h:113
ops_internal.h
ff_sws_ops_compile
int ff_sws_ops_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
Compile a list of operations using the best available backend.
lev
static LevelCodes lev[4+3+3]
Definition: clearvideo.c:80
SwsOp
Definition: ops.h:186
ff_sws_img_shift
static av_const SwsImg ff_sws_img_shift(const SwsImg *base, const int y)
Definition: graph.h:45
av_cmp_q
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:89
ret
ret
Definition: filter_design.txt:187
bswap.h
backend_murder
const SwsOpBackend backend_murder
Definition: ops_memcpy.c:129
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
SWS_MASK_MAT4
@ SWS_MASK_MAT4
Definition: ops.h:180
av_malloc
void * av_malloc(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:98
SwsOpList::dst
SwsFormat dst
Definition: ops.h:234
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:62
SwsCompiledOp
Definition: ops_internal.h:92
SWS_MASK_MAT3
@ SWS_MASK_MAT3
Definition: ops.h:174
SWS_PIXEL_TYPE_NB
@ SWS_PIXEL_TYPE_NB
Definition: ops.h:36
SwsComps
Definition: ops.h:89
ff_sws_compile_pass
int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags, SwsFormat dst, SwsPass *input, SwsPass **output)
Resolves an operation list to a graph pass.
AVRational::den
int den
Denominator.
Definition: rational.h:60
SWS_COMP_SWAPPED
@ SWS_COMP_SWAPPED
Definition: ops.h:76
ff_sws_pixel_type_name
const char * ff_sws_pixel_type_name(SwsPixelType type)
Definition: ops.c:49
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:49
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
av_mul_q
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition: rational.c:80
SWS_COMP_EXACT
@ SWS_COMP_EXACT
Definition: ops.h:74
describe_lin_mask
static const char * describe_lin_mask(uint32_t mask)
Definition: ops.c:568
mem.h
SwsGraph
Filter graph, which represents a 'baked' pixel format conversion.
Definition: graph.h:108
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
SwsImg::fmt
enum AVPixelFormat fmt
Definition: graph.h:34
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:273
av_add_q
AVRational av_add_q(AVRational b, AVRational c)
Add two rationals.
Definition: rational.c:93
ff_sws_pack_op_decode
static void ff_sws_pack_op_decode(const SwsOp *op, uint64_t mask[4], int shift[4])
Definition: ops_internal.h:40
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:60
ff_sws_op_list_print
void ff_sws_op_list_print(void *log, int lev, const SwsOpList *ops)
Print out the contents of an operation list.
Definition: ops.c:641
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2070
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:229
av_bswap16
#define av_bswap16
Definition: bswap.h:28
SwsOpExec::slice_y
int32_t slice_y
Definition: ops_internal.h:73
SwsContext
Main external API structure.
Definition: swscale.h:189
snprintf
#define snprintf
Definition: snprintf.h:34
SwsOpExec::out_bump
ptrdiff_t out_bump[4]
Definition: ops_internal.h:69
read
static uint32_t BS_FUNC() read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
Definition: bitstream_template.h:239
SwsImg::data
uint8_t * data[4]
Definition: graph.h:35
ff_fmt_clear
static void ff_fmt_clear(SwsFormat *fmt)
Definition: format.h:88
min
float min
Definition: vorbis_enc_data.h:429