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/avstring.h"
23 #include "libavutil/bswap.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/rational.h"
26 #include "libavutil/refstruct.h"
27 
28 #include "ops.h"
29 #include "ops_internal.h"
30 
31 extern const SwsOpBackend backend_c;
32 extern const SwsOpBackend backend_murder;
33 extern const SwsOpBackend backend_x86;
34 
35 const SwsOpBackend * const ff_sws_op_backends[] = {
37 #if ARCH_X86_64 && HAVE_X86ASM
38  &backend_x86,
39 #endif
40  &backend_c,
41  NULL
42 };
43 
44 #define RET(x) \
45  do { \
46  if ((ret = (x)) < 0) \
47  return ret; \
48  } while (0)
49 
51 {
52  switch (type) {
53  case SWS_PIXEL_U8: return "u8";
54  case SWS_PIXEL_U16: return "u16";
55  case SWS_PIXEL_U32: return "u32";
56  case SWS_PIXEL_F32: return "f32";
57  case SWS_PIXEL_NONE: return "none";
58  case SWS_PIXEL_TYPE_NB: break;
59  }
60 
61  av_unreachable("Invalid pixel type!");
62  return "ERR";
63 }
64 
66 {
67  switch (type) {
68  case SWS_PIXEL_U8: return sizeof(uint8_t);
69  case SWS_PIXEL_U16: return sizeof(uint16_t);
70  case SWS_PIXEL_U32: return sizeof(uint32_t);
71  case SWS_PIXEL_F32: return sizeof(float);
72  case SWS_PIXEL_NONE: break;
73  case SWS_PIXEL_TYPE_NB: break;
74  }
75 
76  av_unreachable("Invalid pixel type!");
77  return 0;
78 }
79 
81 {
82  switch (type) {
83  case SWS_PIXEL_U8:
84  case SWS_PIXEL_U16:
85  case SWS_PIXEL_U32:
86  return true;
87  case SWS_PIXEL_F32:
88  return false;
89  case SWS_PIXEL_NONE:
90  case SWS_PIXEL_TYPE_NB: break;
91  }
92 
93  av_unreachable("Invalid pixel type!");
94  return false;
95 }
96 
97 /* biased towards `a` */
99 {
100  return av_cmp_q(a, b) == 1 ? b : a;
101 }
102 
104 {
105  return av_cmp_q(a, b) == -1 ? b : a;
106 }
107 
109 {
110  uint64_t mask[4];
111  int shift[4];
112 
113  switch (op->op) {
114  case SWS_OP_READ:
115  case SWS_OP_WRITE:
116  return;
117  case SWS_OP_UNPACK: {
120  unsigned val = x[0].num;
121  for (int i = 0; i < 4; i++)
122  x[i] = Q((val >> shift[i]) & mask[i]);
123  return;
124  }
125  case SWS_OP_PACK: {
128  unsigned val = 0;
129  for (int i = 0; i < 4; i++)
130  val |= (x[i].num & mask[i]) << shift[i];
131  x[0] = Q(val);
132  return;
133  }
134  case SWS_OP_SWAP_BYTES:
136  switch (ff_sws_pixel_type_size(op->type)) {
137  case 2:
138  for (int i = 0; i < 4; i++)
139  x[i].num = av_bswap16(x[i].num);
140  break;
141  case 4:
142  for (int i = 0; i < 4; i++)
143  x[i].num = av_bswap32(x[i].num);
144  break;
145  }
146  return;
147  case SWS_OP_CLEAR:
148  for (int i = 0; i < 4; i++) {
149  if (op->c.q4[i].den)
150  x[i] = op->c.q4[i];
151  }
152  return;
153  case SWS_OP_LSHIFT: {
155  AVRational mult = Q(1 << op->c.u);
156  for (int i = 0; i < 4; i++)
157  x[i] = x[i].den ? av_mul_q(x[i], mult) : x[i];
158  return;
159  }
160  case SWS_OP_RSHIFT: {
162  for (int i = 0; i < 4; i++)
163  x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->c.u) : x[i];
164  return;
165  }
166  case SWS_OP_SWIZZLE: {
167  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
168  for (int i = 0; i < 4; i++)
169  x[i] = orig[op->swizzle.in[i]];
170  return;
171  }
172  case SWS_OP_CONVERT:
173  if (ff_sws_pixel_type_is_int(op->convert.to)) {
174  const AVRational scale = ff_sws_pixel_expand(op->type, op->convert.to);
175  for (int i = 0; i < 4; i++) {
176  x[i] = x[i].den ? Q(x[i].num / x[i].den) : x[i];
177  if (op->convert.expand)
178  x[i] = av_mul_q(x[i], scale);
179  }
180  }
181  return;
182  case SWS_OP_DITHER:
184  for (int i = 0; i < 4; i++)
185  x[i] = x[i].den ? av_add_q(x[i], av_make_q(1, 2)) : x[i];
186  return;
187  case SWS_OP_MIN:
188  for (int i = 0; i < 4; i++)
189  x[i] = av_min_q(x[i], op->c.q4[i]);
190  return;
191  case SWS_OP_MAX:
192  for (int i = 0; i < 4; i++)
193  x[i] = av_max_q(x[i], op->c.q4[i]);
194  return;
195  case SWS_OP_LINEAR: {
197  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
198  for (int i = 0; i < 4; i++) {
199  AVRational sum = op->lin.m[i][4];
200  for (int j = 0; j < 4; j++)
201  sum = av_add_q(sum, av_mul_q(orig[j], op->lin.m[i][j]));
202  x[i] = sum;
203  }
204  return;
205  }
206  case SWS_OP_SCALE:
207  for (int i = 0; i < 4; i++)
208  x[i] = x[i].den ? av_mul_q(x[i], op->c.q) : x[i];
209  return;
210  }
211 
212  av_unreachable("Invalid operation type!");
213 }
214 
215 /* merge_comp_flags() forms a monoid with flags_identity as the null element */
216 static const unsigned flags_identity = SWS_COMP_ZERO | SWS_COMP_EXACT;
217 static unsigned merge_comp_flags(unsigned a, unsigned b)
218 {
219  const unsigned flags_or = SWS_COMP_GARBAGE;
220  const unsigned flags_and = SWS_COMP_ZERO | SWS_COMP_EXACT;
221  return ((a & b) & flags_and) | ((a | b) & flags_or);
222 }
223 
224 /* Infer + propagate known information about components */
226 {
227  SwsComps next = { .unused = {true, true, true, true} };
228  SwsComps prev = { .flags = {
230  }};
231 
232  /* Forwards pass, propagates knowledge about the incoming pixel values */
233  for (int n = 0; n < ops->num_ops; n++) {
234  SwsOp *op = &ops->ops[n];
235 
236  switch (op->op) {
237  case SWS_OP_READ:
238  case SWS_OP_LINEAR:
239  case SWS_OP_SWAP_BYTES:
240  case SWS_OP_UNPACK:
241  break; /* special cases, handled below */
242  default:
243  memcpy(op->comps.min, prev.min, sizeof(prev.min));
244  memcpy(op->comps.max, prev.max, sizeof(prev.max));
245  ff_sws_apply_op_q(op, op->comps.min);
246  ff_sws_apply_op_q(op, op->comps.max);
247  break;
248  }
249 
250  switch (op->op) {
251  case SWS_OP_READ:
252  /* Active components are taken from the user-provided values,
253  * other components are explicitly stripped */
254  for (int i = 0; i < op->rw.elems; i++) {
255  const int idx = op->rw.packed ? i : ops->order_src.in[i];
256  op->comps.flags[i] = ops->comps_src.flags[idx];
257  op->comps.min[i] = ops->comps_src.min[idx];
258  op->comps.max[i] = ops->comps_src.max[idx];
259  }
260  for (int i = op->rw.elems; i < 4; i++) {
261  op->comps.flags[i] = prev.flags[i];
262  op->comps.min[i] = prev.min[i];
263  op->comps.max[i] = prev.max[i];
264  }
265  break;
266  case SWS_OP_SWAP_BYTES:
267  for (int i = 0; i < 4; i++) {
268  op->comps.flags[i] = prev.flags[i] ^ SWS_COMP_SWAPPED;
269  op->comps.min[i] = prev.min[i];
270  op->comps.max[i] = prev.max[i];
271  }
272  break;
273  case SWS_OP_WRITE:
274  for (int i = 0; i < op->rw.elems; i++)
275  av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
276  /* fall through */
277  case SWS_OP_LSHIFT:
278  case SWS_OP_RSHIFT:
279  case SWS_OP_MIN:
280  case SWS_OP_MAX:
281  /* Linearly propagate flags per component */
282  for (int i = 0; i < 4; i++)
283  op->comps.flags[i] = prev.flags[i];
284  break;
285  case SWS_OP_DITHER:
286  /* Strip zero flag because of the nonzero dithering offset */
287  for (int i = 0; i < 4; i++)
288  op->comps.flags[i] = prev.flags[i] & ~SWS_COMP_ZERO;
289  break;
290  case SWS_OP_UNPACK:
291  for (int i = 0; i < 4; i++) {
292  const int pattern = op->pack.pattern[i];
293  if (pattern) {
294  av_assert1(pattern < 32);
295  op->comps.flags[i] = prev.flags[0];
296  op->comps.min[i] = Q(0);
297  op->comps.max[i] = Q((1ULL << pattern) - 1);
298  } else
299  op->comps.flags[i] = SWS_COMP_GARBAGE;
300  }
301  break;
302  case SWS_OP_PACK: {
303  unsigned flags = flags_identity;
304  for (int i = 0; i < 4; i++) {
305  if (op->pack.pattern[i])
306  flags = merge_comp_flags(flags, prev.flags[i]);
307  if (i > 0) /* clear remaining comps for sanity */
308  op->comps.flags[i] = SWS_COMP_GARBAGE;
309  }
310  op->comps.flags[0] = flags;
311  break;
312  }
313  case SWS_OP_CLEAR:
314  for (int i = 0; i < 4; i++) {
315  if (op->c.q4[i].den) {
316  op->comps.flags[i] = 0;
317  if (op->c.q4[i].num == 0)
318  op->comps.flags[i] |= SWS_COMP_ZERO;
319  if (op->c.q4[i].den == 1)
320  op->comps.flags[i] |= SWS_COMP_EXACT;
321  } else {
322  op->comps.flags[i] = prev.flags[i];
323  }
324  }
325  break;
326  case SWS_OP_SWIZZLE:
327  for (int i = 0; i < 4; i++)
328  op->comps.flags[i] = prev.flags[op->swizzle.in[i]];
329  break;
330  case SWS_OP_CONVERT:
331  for (int i = 0; i < 4; i++) {
332  op->comps.flags[i] = prev.flags[i];
333  if (ff_sws_pixel_type_is_int(op->convert.to))
334  op->comps.flags[i] |= SWS_COMP_EXACT;
335  }
336  break;
337  case SWS_OP_LINEAR:
338  for (int i = 0; i < 4; i++) {
339  unsigned flags = flags_identity;
340  AVRational min = Q(0), max = Q(0);
341  for (int j = 0; j < 4; j++) {
342  const AVRational k = op->lin.m[i][j];
343  AVRational mink = av_mul_q(prev.min[j], k);
344  AVRational maxk = av_mul_q(prev.max[j], k);
345  if (k.num) {
346  flags = merge_comp_flags(flags, prev.flags[j]);
347  if (k.den != 1) /* fractional coefficient */
348  flags &= ~SWS_COMP_EXACT;
349  if (k.num < 0)
350  FFSWAP(AVRational, mink, maxk);
351  min = av_add_q(min, mink);
352  max = av_add_q(max, maxk);
353  }
354  }
355  if (op->lin.m[i][4].num) { /* nonzero offset */
356  flags &= ~SWS_COMP_ZERO;
357  if (op->lin.m[i][4].den != 1) /* fractional offset */
358  flags &= ~SWS_COMP_EXACT;
359  min = av_add_q(min, op->lin.m[i][4]);
360  max = av_add_q(max, op->lin.m[i][4]);
361  }
362  op->comps.flags[i] = flags;
363  op->comps.min[i] = min;
364  op->comps.max[i] = max;
365  }
366  break;
367  case SWS_OP_SCALE:
368  for (int i = 0; i < 4; i++) {
369  op->comps.flags[i] = prev.flags[i];
370  if (op->c.q.den != 1) /* fractional scale */
371  op->comps.flags[i] &= ~SWS_COMP_EXACT;
372  if (op->c.q.num < 0)
373  FFSWAP(AVRational, op->comps.min[i], op->comps.max[i]);
374  }
375  break;
376 
377  case SWS_OP_INVALID:
378  case SWS_OP_TYPE_NB:
379  av_unreachable("Invalid operation type!");
380  }
381 
382  prev = op->comps;
383  }
384 
385  /* Backwards pass, solves for component dependencies */
386  for (int n = ops->num_ops - 1; n >= 0; n--) {
387  SwsOp *op = &ops->ops[n];
388 
389  switch (op->op) {
390  case SWS_OP_READ:
391  case SWS_OP_WRITE:
392  for (int i = 0; i < op->rw.elems; i++)
393  op->comps.unused[i] = op->op == SWS_OP_READ;
394  for (int i = op->rw.elems; i < 4; i++)
395  op->comps.unused[i] = next.unused[i];
396  break;
397  case SWS_OP_SWAP_BYTES:
398  case SWS_OP_LSHIFT:
399  case SWS_OP_RSHIFT:
400  case SWS_OP_CONVERT:
401  case SWS_OP_DITHER:
402  case SWS_OP_MIN:
403  case SWS_OP_MAX:
404  case SWS_OP_SCALE:
405  for (int i = 0; i < 4; i++)
406  op->comps.unused[i] = next.unused[i];
407  break;
408  case SWS_OP_UNPACK: {
409  bool unused = true;
410  for (int i = 0; i < 4; i++) {
411  if (op->pack.pattern[i])
412  unused &= next.unused[i];
413  op->comps.unused[i] = i > 0;
414  }
415  op->comps.unused[0] = unused;
416  break;
417  }
418  case SWS_OP_PACK:
419  for (int i = 0; i < 4; i++) {
420  if (op->pack.pattern[i])
421  op->comps.unused[i] = next.unused[0];
422  else
423  op->comps.unused[i] = true;
424  }
425  break;
426  case SWS_OP_CLEAR:
427  for (int i = 0; i < 4; i++) {
428  if (op->c.q4[i].den)
429  op->comps.unused[i] = true;
430  else
431  op->comps.unused[i] = next.unused[i];
432  }
433  break;
434  case SWS_OP_SWIZZLE: {
435  bool unused[4] = { true, true, true, true };
436  for (int i = 0; i < 4; i++)
437  unused[op->swizzle.in[i]] &= next.unused[i];
438  for (int i = 0; i < 4; i++)
439  op->comps.unused[i] = unused[i];
440  break;
441  }
442  case SWS_OP_LINEAR:
443  for (int j = 0; j < 4; j++) {
444  bool unused = true;
445  for (int i = 0; i < 4; i++) {
446  if (op->lin.m[i][j].num)
447  unused &= next.unused[i];
448  }
449  op->comps.unused[j] = unused;
450  }
451  break;
452  }
453 
454  next = op->comps;
455  }
456 }
457 
458 static void op_uninit(SwsOp *op)
459 {
460  switch (op->op) {
461  case SWS_OP_DITHER:
462  av_refstruct_unref(&op->dither.matrix);
463  break;
464  }
465 
466  *op = (SwsOp) {0};
467 }
468 
470 {
471  SwsOpList *ops = av_mallocz(sizeof(SwsOpList));
472  if (!ops)
473  return NULL;
474 
475  ops->order_src = ops->order_dst = SWS_SWIZZLE(0, 1, 2, 3);
476  ff_fmt_clear(&ops->src);
477  ff_fmt_clear(&ops->dst);
478  return ops;
479 }
480 
482 {
483  SwsOpList *ops = *p_ops;
484  if (!ops)
485  return;
486 
487  for (int i = 0; i < ops->num_ops; i++)
488  op_uninit(&ops->ops[i]);
489 
490  av_freep(&ops->ops);
491  av_free(ops);
492  *p_ops = NULL;
493 }
494 
496 {
497  SwsOpList *copy = av_malloc(sizeof(*copy));
498  if (!copy)
499  return NULL;
500 
501  int num = ops->num_ops;
502  if (num)
503  num = 1 << av_ceil_log2(num);
504 
505  *copy = *ops;
506  copy->ops = av_memdup(ops->ops, num * sizeof(ops->ops[0]));
507  if (!copy->ops) {
508  av_free(copy);
509  return NULL;
510  }
511 
512  for (int i = 0; i < ops->num_ops; i++) {
513  const SwsOp *op = &ops->ops[i];
514  switch (op->op) {
515  case SWS_OP_DITHER:
516  av_refstruct_ref(copy->ops[i].dither.matrix);
517  break;
518  }
519  }
520 
521  return copy;
522 }
523 
524 void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
525 {
526  const int end = ops->num_ops - count;
527  av_assert2(index >= 0 && count >= 0 && index + count <= ops->num_ops);
528  op_uninit(&ops->ops[index]);
529  for (int i = index; i < end; i++)
530  ops->ops[i] = ops->ops[i + count];
531  ops->num_ops = end;
532 }
533 
535 {
536  void *ret = av_dynarray2_add((void **) &ops->ops, &ops->num_ops, sizeof(*op), NULL);
537  if (!ret) {
538  op_uninit(op);
539  return AVERROR(ENOMEM);
540  }
541 
542  for (int i = ops->num_ops - 1; i > index; i--)
543  ops->ops[i] = ops->ops[i - 1];
544  ops->ops[index] = *op;
545  return 0;
546 }
547 
549 {
550  return ff_sws_op_list_insert_at(ops, ops->num_ops, op);
551 }
552 
554 {
555  if (!ops->num_ops)
556  return true;
557 
558  const SwsOp *read = &ops->ops[0];
559  const SwsOp *write = &ops->ops[1];
560  if (ops->num_ops != 2 ||
561  read->op != SWS_OP_READ ||
562  write->op != SWS_OP_WRITE ||
563  read->type != write->type ||
564  read->rw.packed != write->rw.packed ||
565  read->rw.elems != write->rw.elems ||
566  read->rw.frac != write->rw.frac)
567  return false;
568 
569  /**
570  * Note that this check is unlikely to ever be hit in practice, since it
571  * would imply the existence of planar formats with different plane orders
572  * between them, e.g. rgbap <-> gbrap, which doesn't currently exist.
573  * However, the check is cheap and lets me sleep at night.
574  */
575  const int num_planes = read->rw.packed ? 1 : read->rw.elems;
576  for (int i = 0; i < num_planes; i++) {
577  if (ops->order_src.in[i] != ops->order_dst.in[i])
578  return false;
579  }
580 
581  return true;
582 }
583 
585 {
586  int max_size = 0;
587  for (int i = 0; i < ops->num_ops; i++) {
588  const int size = ff_sws_pixel_type_size(ops->ops[i].type);
589  max_size = FFMAX(max_size, size);
590  }
591 
592  return max_size;
593 }
594 
596 {
597  uint32_t mask = 0;
598  for (int i = 0; i < 4; i++) {
599  for (int j = 0; j < 5; j++) {
600  if (av_cmp_q(c.m[i][j], Q(i == j)))
601  mask |= SWS_MASK(i, j);
602  }
603  }
604  return mask;
605 }
606 
607 static const char *describe_lin_mask(uint32_t mask)
608 {
609  /* Try to be fairly descriptive without assuming too much */
610  static const struct {
611  char name[24];
612  uint32_t mask;
613  } patterns[] = {
614  { "noop", 0 },
615  { "luma", SWS_MASK_LUMA },
616  { "alpha", SWS_MASK_ALPHA },
617  { "luma+alpha", SWS_MASK_LUMA | SWS_MASK_ALPHA },
618  { "dot3", 0x7 },
619  { "dot4", 0xF },
620  { "row0", SWS_MASK_ROW(0) },
621  { "row0+alpha", SWS_MASK_ROW(0) | SWS_MASK_ALPHA },
622  { "col0", SWS_MASK_COL(0) },
623  { "col0+off3", SWS_MASK_COL(0) | SWS_MASK_OFF3 },
624  { "off3", SWS_MASK_OFF3 },
625  { "off3+alpha", SWS_MASK_OFF3 | SWS_MASK_ALPHA },
626  { "diag3", SWS_MASK_DIAG3 },
627  { "diag4", SWS_MASK_DIAG4 },
628  { "diag3+alpha", SWS_MASK_DIAG3 | SWS_MASK_ALPHA },
629  { "diag3+off3", SWS_MASK_DIAG3 | SWS_MASK_OFF3 },
630  { "diag3+off3+alpha", SWS_MASK_DIAG3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
631  { "diag4+off4", SWS_MASK_DIAG4 | SWS_MASK_OFF4 },
632  { "matrix3", SWS_MASK_MAT3 },
633  { "matrix3+off3", SWS_MASK_MAT3 | SWS_MASK_OFF3 },
634  { "matrix3+off3+alpha", SWS_MASK_MAT3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
635  { "matrix4", SWS_MASK_MAT4 },
636  { "matrix4+off4", SWS_MASK_MAT4 | SWS_MASK_OFF4 },
637  };
638 
639  for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
640  if (!(mask & ~patterns[i].mask))
641  return patterns[i].name;
642  }
643 
644  av_unreachable("Invalid linear mask!");
645  return "ERR";
646 }
647 
648 static char describe_comp_flags(unsigned flags)
649 {
650  if (flags & SWS_COMP_GARBAGE)
651  return 'X';
652  else if (flags & SWS_COMP_ZERO)
653  return '0';
654  else if (flags & SWS_COMP_SWAPPED)
655  return 'z';
656  else if (flags & SWS_COMP_EXACT)
657  return '+';
658  else
659  return '.';
660 }
661 
662 static const char *describe_order(SwsSwizzleOp order, int planes, char buf[32])
663 {
664  if (order.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
665  return "";
666 
667  av_strlcpy(buf, ", via {", 32);
668  for (int i = 0; i < planes; i++)
669  av_strlcatf(buf, 32, "%s%d", i ? ", " : "", order.in[i]);
670  av_strlcat(buf, "}", 32);
671  return buf;
672 }
673 
674 static const char *print_q(const AVRational q, char buf[], int buf_len)
675 {
676  if (!q.den) {
677  return q.num > 0 ? "inf" : q.num < 0 ? "-inf" : "nan";
678  } else if (q.den == 1) {
679  snprintf(buf, buf_len, "%d", q.num);
680  return buf;
681  } else if (abs(q.num) > 1000 || abs(q.den) > 1000) {
682  snprintf(buf, buf_len, "%f", av_q2d(q));
683  return buf;
684  } else {
685  snprintf(buf, buf_len, "%d/%d", q.num, q.den);
686  return buf;
687  }
688 }
689 
690 #define PRINTQ(q) print_q(q, (char[32]){0}, sizeof(char[32]))
691 
692 void ff_sws_op_list_print(void *log, int lev, const SwsOpList *ops)
693 {
694  if (!ops->num_ops) {
695  av_log(log, lev, " (empty)\n");
696  return;
697  }
698 
699  for (int i = 0; i < ops->num_ops; i++) {
700  const SwsOp *op = &ops->ops[i];
701  char buf[32];
702 
703  av_log(log, lev, " [%3s %c%c%c%c -> %c%c%c%c] ",
704  ff_sws_pixel_type_name(op->type),
705  op->comps.unused[0] ? 'X' : '.',
706  op->comps.unused[1] ? 'X' : '.',
707  op->comps.unused[2] ? 'X' : '.',
708  op->comps.unused[3] ? 'X' : '.',
709  describe_comp_flags(op->comps.flags[0]),
710  describe_comp_flags(op->comps.flags[1]),
711  describe_comp_flags(op->comps.flags[2]),
712  describe_comp_flags(op->comps.flags[3]));
713 
714  switch (op->op) {
715  case SWS_OP_INVALID:
716  av_log(log, lev, "SWS_OP_INVALID\n");
717  break;
718  case SWS_OP_READ:
719  case SWS_OP_WRITE:
720  av_log(log, lev, "%-20s: %d elem(s) %s >> %d%s\n",
721  op->op == SWS_OP_READ ? "SWS_OP_READ"
722  : "SWS_OP_WRITE",
723  op->rw.elems, op->rw.packed ? "packed" : "planar",
724  op->rw.frac,
725  describe_order(op->op == SWS_OP_READ ? ops->order_src
726  : ops->order_dst,
727  op->rw.packed ? 1 : op->rw.elems, buf));
728  break;
729  case SWS_OP_SWAP_BYTES:
730  av_log(log, lev, "SWS_OP_SWAP_BYTES\n");
731  break;
732  case SWS_OP_LSHIFT:
733  av_log(log, lev, "%-20s: << %u\n", "SWS_OP_LSHIFT", op->c.u);
734  break;
735  case SWS_OP_RSHIFT:
736  av_log(log, lev, "%-20s: >> %u\n", "SWS_OP_RSHIFT", op->c.u);
737  break;
738  case SWS_OP_PACK:
739  case SWS_OP_UNPACK:
740  av_log(log, lev, "%-20s: {%d %d %d %d}\n",
741  op->op == SWS_OP_PACK ? "SWS_OP_PACK"
742  : "SWS_OP_UNPACK",
743  op->pack.pattern[0], op->pack.pattern[1],
744  op->pack.pattern[2], op->pack.pattern[3]);
745  break;
746  case SWS_OP_CLEAR:
747  av_log(log, lev, "%-20s: {%s %s %s %s}\n", "SWS_OP_CLEAR",
748  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
749  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
750  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
751  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
752  break;
753  case SWS_OP_SWIZZLE:
754  av_log(log, lev, "%-20s: %d%d%d%d\n", "SWS_OP_SWIZZLE",
755  op->swizzle.x, op->swizzle.y, op->swizzle.z, op->swizzle.w);
756  break;
757  case SWS_OP_CONVERT:
758  av_log(log, lev, "%-20s: %s -> %s%s\n", "SWS_OP_CONVERT",
759  ff_sws_pixel_type_name(op->type),
760  ff_sws_pixel_type_name(op->convert.to),
761  op->convert.expand ? " (expand)" : "");
762  break;
763  case SWS_OP_DITHER:
764  av_log(log, lev, "%-20s: %dx%d matrix + {%d %d %d %d}\n", "SWS_OP_DITHER",
765  1 << op->dither.size_log2, 1 << op->dither.size_log2,
766  op->dither.y_offset[0], op->dither.y_offset[1],
767  op->dither.y_offset[2], op->dither.y_offset[3]);
768  break;
769  case SWS_OP_MIN:
770  av_log(log, lev, "%-20s: x <= {%s %s %s %s}\n", "SWS_OP_MIN",
771  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
772  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
773  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
774  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
775  break;
776  case SWS_OP_MAX:
777  av_log(log, lev, "%-20s: {%s %s %s %s} <= x\n", "SWS_OP_MAX",
778  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
779  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
780  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
781  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
782  break;
783  case SWS_OP_LINEAR:
784  av_log(log, lev, "%-20s: %s [[%s %s %s %s %s] "
785  "[%s %s %s %s %s] "
786  "[%s %s %s %s %s] "
787  "[%s %s %s %s %s]]\n",
788  "SWS_OP_LINEAR", describe_lin_mask(op->lin.mask),
789  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]),
790  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]),
791  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]),
792  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]));
793  break;
794  case SWS_OP_SCALE:
795  av_log(log, lev, "%-20s: * %s\n", "SWS_OP_SCALE",
796  PRINTQ(op->c.q));
797  break;
798  case SWS_OP_TYPE_NB:
799  break;
800  }
801 
802  if (op->comps.min[0].den || op->comps.min[1].den ||
803  op->comps.min[2].den || op->comps.min[3].den ||
804  op->comps.max[0].den || op->comps.max[1].den ||
805  op->comps.max[2].den || op->comps.max[3].den)
806  {
807  av_log(log, AV_LOG_TRACE, " min: {%s, %s, %s, %s}, max: {%s, %s, %s, %s}\n",
808  PRINTQ(op->comps.min[0]), PRINTQ(op->comps.min[1]),
809  PRINTQ(op->comps.min[2]), PRINTQ(op->comps.min[3]),
810  PRINTQ(op->comps.max[0]), PRINTQ(op->comps.max[1]),
811  PRINTQ(op->comps.max[2]), PRINTQ(op->comps.max[3]));
812  }
813 
814  }
815 
816  av_log(log, lev, " (X = unused, z = byteswapped, + = exact, 0 = zero)\n");
817 }
818 
820  const SwsOpList *ops, SwsCompiledOp *out)
821 {
822  SwsOpList *copy, rest;
823  SwsCompiledOp compiled = {0};
824  int ret = 0;
825 
827  if (!copy)
828  return AVERROR(ENOMEM);
829 
830  /* Ensure these are always set during compilation */
832 
833  /* Make an on-stack copy of `ops` to ensure we can still properly clean up
834  * the copy afterwards */
835  rest = *copy;
836 
837  ret = backend->compile(ctx, &rest, &compiled);
838  if (ret < 0) {
839  int msg_lev = ret == AVERROR(ENOTSUP) ? AV_LOG_TRACE : AV_LOG_ERROR;
840  av_log(ctx, msg_lev, "Backend '%s' failed to compile operations: %s\n",
841  backend->name, av_err2str(ret));
842  if (rest.num_ops != ops->num_ops) {
843  av_log(ctx, msg_lev, "Uncompiled remainder:\n");
844  ff_sws_op_list_print(ctx, msg_lev, &rest);
845  }
846  } else {
847  *out = compiled;
848  }
849 
851  return ret;
852 }
853 
855 {
856  for (int n = 0; ff_sws_op_backends[n]; n++) {
857  const SwsOpBackend *backend = ff_sws_op_backends[n];
858  if (ff_sws_ops_compile_backend(ctx, backend, ops, out) < 0)
859  continue;
860 
861  av_log(ctx, AV_LOG_VERBOSE, "Compiled using backend '%s': "
862  "block size = %d, over-read = %d, over-write = %d, cpu flags = 0x%x\n",
863  backend->name, out->block_size, out->over_read, out->over_write,
864  out->cpu_flags);
865  return 0;
866  }
867 
868  av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
870  return AVERROR(ENOTSUP);
871 }
872 
873 typedef struct SwsOpPass {
875  SwsOpExec exec_base;
876  int num_blocks;
877  int tail_off_in;
878  int tail_off_out;
879  int tail_size_in;
880  int tail_size_out;
881  int planes_in;
882  int planes_out;
883  int pixel_bits_in;
884  int pixel_bits_out;
885  int idx_in[4];
886  int idx_out[4];
887  bool memcpy_in;
888  bool memcpy_out;
889 } SwsOpPass;
890 
891 static void op_pass_free(void *ptr)
892 {
893  SwsOpPass *p = ptr;
894  if (!p)
895  return;
896 
897  if (p->comp.free)
898  p->comp.free(p->comp.priv);
899 
900  av_free(p);
901 }
902 
903 static inline SwsImg img_shift_idx(const SwsImg *base, const int y,
904  const int plane_idx[4])
905 {
906  SwsImg img = *base;
907  for (int i = 0; i < 4; i++) {
908  const int idx = plane_idx[i];
909  if (idx >= 0) {
910  const int yshift = y >> ff_fmt_vshift(base->fmt, idx);
911  img.data[i] = base->data[idx] + yshift * base->linesize[idx];
912  } else {
913  img.data[i] = NULL;
914  }
915  }
916  return img;
917 }
918 
919 static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
920  const SwsPass *pass)
921 {
922  const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(in_base->fmt);
923  const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out_base->fmt);
924 
925  SwsOpPass *p = pass->priv;
926  SwsOpExec *exec = &p->exec_base;
927  const SwsCompiledOp *comp = &p->comp;
928  const int block_size = comp->block_size;
929  p->num_blocks = (pass->width + block_size - 1) / block_size;
930 
931  /* Set up main loop parameters */
932  const int aligned_w = p->num_blocks * block_size;
933  const int safe_width = (p->num_blocks - 1) * block_size;
934  const int tail_size = pass->width - safe_width;
935  p->tail_off_in = safe_width * p->pixel_bits_in >> 3;
936  p->tail_off_out = safe_width * p->pixel_bits_out >> 3;
937  p->tail_size_in = tail_size * p->pixel_bits_in >> 3;
938  p->tail_size_out = tail_size * p->pixel_bits_out >> 3;
939  p->memcpy_in = false;
940  p->memcpy_out = false;
941 
942  const SwsImg in = img_shift_idx(in_base, 0, p->idx_in);
943  const SwsImg out = img_shift_idx(out_base, 0, p->idx_out);
944 
945  for (int i = 0; i < p->planes_in; i++) {
946  const int idx = p->idx_in[i];
947  const int sub_x = (idx == 1 || idx == 2) ? indesc->log2_chroma_w : 0;
948  const int plane_w = (aligned_w + sub_x) >> sub_x;
949  const int plane_pad = (comp->over_read + sub_x) >> sub_x;
950  const int plane_size = plane_w * p->pixel_bits_in >> 3;
951  p->memcpy_in |= plane_size + plane_pad > in.linesize[i];
952  exec->in_stride[i] = in.linesize[i];
953  }
954 
955  for (int i = 0; i < p->planes_out; i++) {
956  const int idx = p->idx_out[i];
957  const int sub_x = (idx == 1 || idx == 2) ? outdesc->log2_chroma_w : 0;
958  const int plane_w = (aligned_w + sub_x) >> sub_x;
959  const int plane_pad = (comp->over_write + sub_x) >> sub_x;
960  const int plane_size = plane_w * p->pixel_bits_out >> 3;
961  p->memcpy_out |= plane_size + plane_pad > out.linesize[i];
962  exec->out_stride[i] = out.linesize[i];
963  }
964 
965  /* Pre-fill pointer bump for the main section only; this value does not
966  * matter at all for the tail / last row handlers because they only ever
967  * process a single line */
968  const int blocks_main = p->num_blocks - p->memcpy_out;
969  for (int i = 0; i < 4; i++) {
970  exec->in_bump[i] = in.linesize[i] - blocks_main * exec->block_size_in;
971  exec->out_bump[i] = out.linesize[i] - blocks_main * exec->block_size_out;
972  }
973 }
974 
975 /* Dispatch kernel over the last column of the image using memcpy */
976 static av_always_inline void
977 handle_tail(const SwsOpPass *p, SwsOpExec *exec,
978  const SwsImg *out_base, const bool copy_out,
979  const SwsImg *in_base, const bool copy_in,
980  int y, const int h)
981 {
982  DECLARE_ALIGNED_64(uint8_t, tmp)[2][4][sizeof(uint32_t[128])];
983 
984  const SwsCompiledOp *comp = &p->comp;
985  const int tail_size_in = p->tail_size_in;
986  const int tail_size_out = p->tail_size_out;
987  const int bx = p->num_blocks - 1;
988 
989  SwsImg in = img_shift_idx(in_base, y, p->idx_in);
990  SwsImg out = img_shift_idx(out_base, y, p->idx_out);
991  for (int i = 0; i < p->planes_in; i++) {
992  in.data[i] += p->tail_off_in;
993  if (copy_in) {
994  exec->in[i] = (void *) tmp[0][i];
995  exec->in_stride[i] = sizeof(tmp[0][i]);
996  } else {
997  exec->in[i] = in.data[i];
998  }
999  }
1000 
1001  for (int i = 0; i < p->planes_out; i++) {
1002  out.data[i] += p->tail_off_out;
1003  if (copy_out) {
1004  exec->out[i] = (void *) tmp[1][i];
1005  exec->out_stride[i] = sizeof(tmp[1][i]);
1006  } else {
1007  exec->out[i] = out.data[i];
1008  }
1009  }
1010 
1011  for (int y_end = y + h; y < y_end; y++) {
1012  if (copy_in) {
1013  for (int i = 0; i < p->planes_in; i++) {
1014  av_assert2(tmp[0][i] + tail_size_in < (uint8_t *) tmp[1]);
1015  memcpy(tmp[0][i], in.data[i], tail_size_in);
1016  in.data[i] += in.linesize[i];
1017  }
1018  }
1019 
1020  comp->func(exec, comp->priv, bx, y, p->num_blocks, y + 1);
1021 
1022  if (copy_out) {
1023  for (int i = 0; i < p->planes_out; i++) {
1024  av_assert2(tmp[1][i] + tail_size_out < (uint8_t *) tmp[2]);
1025  memcpy(out.data[i], tmp[1][i], tail_size_out);
1026  out.data[i] += out.linesize[i];
1027  }
1028  }
1029 
1030  for (int i = 0; i < 4; i++) {
1031  if (!copy_in)
1032  exec->in[i] += in.linesize[i];
1033  if (!copy_out)
1034  exec->out[i] += out.linesize[i];
1035  }
1036  }
1037 }
1038 
1039 static void op_pass_run(const SwsImg *out_base, const SwsImg *in_base,
1040  const int y, const int h, const SwsPass *pass)
1041 {
1042  const SwsOpPass *p = pass->priv;
1043  const SwsCompiledOp *comp = &p->comp;
1044  const SwsImg in = img_shift_idx(in_base, y, p->idx_in);
1045  const SwsImg out = img_shift_idx(out_base, y, p->idx_out);
1046 
1047  /* Fill exec metadata for this slice */
1048  DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
1049  exec.slice_y = y;
1050  exec.slice_h = h;
1051  for (int i = 0; i < 4; i++) {
1052  exec.in[i] = in.data[i];
1053  exec.out[i] = out.data[i];
1054  }
1055 
1056  /**
1057  * To ensure safety, we need to consider the following:
1058  *
1059  * 1. We can overread the input, unless this is the last line of an
1060  * unpadded buffer. All defined operations can handle arbitrary pixel
1061  * input, so overread of arbitrary data is fine.
1062  *
1063  * 2. We can overwrite the output, as long as we don't write more than the
1064  * amount of pixels that fit into one linesize. So we always need to
1065  * memcpy the last column on the output side if unpadded.
1066  *
1067  * 3. For the last row, we also need to memcpy the remainder of the input,
1068  * to avoid reading past the end of the buffer. Note that since we know
1069  * the run() function is called on stripes of the same buffer, we don't
1070  * need to worry about this for the end of a slice.
1071  */
1072 
1073  const int last_slice = y + h == pass->height;
1074  const bool memcpy_in = last_slice && p->memcpy_in;
1075  const bool memcpy_out = p->memcpy_out;
1076  const int num_blocks = p->num_blocks;
1077  const int blocks_main = num_blocks - memcpy_out;
1078  const int h_main = h - memcpy_in;
1079 
1080  /* Handle main section */
1081  comp->func(&exec, comp->priv, 0, y, blocks_main, y + h_main);
1082 
1083  if (memcpy_in) {
1084  /* Safe part of last row */
1085  for (int i = 0; i < 4; i++) {
1086  exec.in[i] += h_main * in.linesize[i];
1087  exec.out[i] += h_main * out.linesize[i];
1088  }
1089  comp->func(&exec, comp->priv, 0, y + h_main, num_blocks - 1, y + h);
1090  }
1091 
1092  /* Handle last column via memcpy, takes over `exec` so call these last */
1093  if (memcpy_out)
1094  handle_tail(p, &exec, out_base, true, in_base, false, y, h_main);
1095  if (memcpy_in)
1096  handle_tail(p, &exec, out_base, memcpy_out, in_base, true, y + h_main, 1);
1097 }
1098 
1099 static int rw_planes(const SwsOp *op)
1100 {
1101  return op->rw.packed ? 1 : op->rw.elems;
1102 }
1103 
1104 static int rw_pixel_bits(const SwsOp *op)
1105 {
1106  const int elems = op->rw.packed ? op->rw.elems : 1;
1107  const int size = ff_sws_pixel_type_size(op->type);
1108  const int bits = 8 >> op->rw.frac;
1109  av_assert1(bits >= 1);
1110  return elems * size * bits;
1111 }
1112 
1113 int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags, SwsFormat dst,
1115 {
1116  SwsContext *ctx = graph->ctx;
1117  SwsOpPass *p = NULL;
1118  const SwsOp *read = &ops->ops[0];
1119  const SwsOp *write = &ops->ops[ops->num_ops - 1];
1120  SwsPass *pass;
1121  int ret;
1122 
1123  /* Check if the whole operation graph is an end-to-end no-op */
1124  if (ff_sws_op_list_is_noop(ops)) {
1125  *output = input;
1126  return 0;
1127  }
1128 
1129  if (ops->num_ops < 2) {
1130  av_log(ctx, AV_LOG_ERROR, "Need at least two operations.\n");
1131  return AVERROR(EINVAL);
1132  }
1133 
1134  if (read->op != SWS_OP_READ || write->op != SWS_OP_WRITE) {
1135  av_log(ctx, AV_LOG_ERROR, "First and last operations must be a read "
1136  "and write, respectively.\n");
1137  return AVERROR(EINVAL);
1138  }
1139 
1142  else
1144 
1145  p = av_mallocz(sizeof(*p));
1146  if (!p)
1147  return AVERROR(ENOMEM);
1148 
1149  ret = ff_sws_ops_compile(ctx, ops, &p->comp);
1150  if (ret < 0)
1151  goto fail;
1152 
1153  p->planes_in = rw_planes(read);
1154  p->planes_out = rw_planes(write);
1155  p->pixel_bits_in = rw_pixel_bits(read);
1156  p->pixel_bits_out = rw_pixel_bits(write);
1157  p->exec_base = (SwsOpExec) {
1158  .width = dst.width,
1159  .height = dst.height,
1160  .block_size_in = p->comp.block_size * p->pixel_bits_in >> 3,
1161  .block_size_out = p->comp.block_size * p->pixel_bits_out >> 3,
1162  };
1163 
1164  for (int i = 0; i < 4; i++) {
1165  p->idx_in[i] = i < p->planes_in ? ops->order_src.in[i] : -1;
1166  p->idx_out[i] = i < p->planes_out ? ops->order_dst.in[i] : -1;
1167  }
1168 
1169  pass = ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height, input,
1170  1, p, op_pass_run);
1171  if (!pass) {
1172  ret = AVERROR(ENOMEM);
1173  goto fail;
1174  }
1175  pass->setup = op_pass_setup;
1176  pass->free = op_pass_free;
1177 
1178  *output = pass;
1179  return 0;
1180 
1181 fail:
1182  op_pass_free(p);
1183  return ret;
1184 }
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:481
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:469
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:495
out
static FILE * out
Definition: movenc.c:55
SwsSwizzleOp::mask
uint32_t mask
Definition: ops.h:126
av_min_q
static AVRational av_min_q(AVRational a, AVRational b)
Definition: ops.c:98
comp
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition: eamad.c:79
SwsOpList::comps_src
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition: ops.h:238
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:595
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:584
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:548
normalize.log
log
Definition: normalize.py:21
mask
int mask
Definition: mediacodecdec_common.c:154
SwsOp::rw
SwsReadWriteOp rw
Definition: ops.h:191
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_MAT3
@ SWS_MASK_MAT3
Definition: ops.h:174
SWS_MASK_OFF3
@ SWS_MASK_OFF3
Definition: ops.h:173
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:226
base
uint8_t base
Definition: vp3data.h:128
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
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
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:65
describe_comp_flags
static char describe_comp_flags(unsigned flags)
Definition: ops.c:648
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
ff_sws_op_backends
const SwsOpBackend *const ff_sws_op_backends[]
Definition: ops.c:35
av_ceil_log2
#define av_ceil_log2
Definition: common.h:97
fail
#define fail()
Definition: checkasm.h:218
SwsOpList::num_ops
int num_ops
Definition: ops.h:224
SWS_MASK_COL
#define SWS_MASK_COL(J)
Definition: ops.h:165
flags_identity
static const unsigned flags_identity
Definition: ops.c:216
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
SwsSwizzleOp
Definition: ops.h:120
ff_sws_pixel_type_is_int
bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: ops.c:80
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:44
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
SWS_MASK_ALPHA
@ SWS_MASK_ALPHA
Definition: ops.h:170
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
SWS_SWIZZLE
#define SWS_SWIZZLE(X, Y, Z, W)
Definition: ops.h:132
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:108
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
static 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:690
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
describe_order
static const char * describe_order(SwsSwizzleOp order, int planes, char buf[32])
Definition: ops.c:662
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:217
ff_sws_op_list_is_noop
bool ff_sws_op_list_is_noop(const SwsOpList *ops)
Returns whether an op list represents a true no-op operation, i.e.
Definition: ops.c:553
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_LUMA
@ SWS_MASK_LUMA
Definition: ops.h:169
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:116
SwsReadWriteOp::frac
uint8_t frac
Definition: ops.h:100
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:524
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:674
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:108
SwsOpList::order_dst
SwsSwizzleOp order_dst
Definition: ops.h:227
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:534
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:241
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:225
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
img
#define img
Definition: vf_colormatrix.c:114
op_uninit
static void op_uninit(SwsOp *op)
Definition: ops.c:458
SWS_OP_FLAG_OPTIMIZE
@ SWS_OP_FLAG_OPTIMIZE
Definition: ops.h:290
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:283
av_assert2
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition: avassert.h:68
ff_fmt_vshift
static av_always_inline av_const int ff_fmt_vshift(enum AVPixelFormat fmt, int plane)
Definition: graph.h:39
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:103
SwsOpList::ops
SwsOp * ops
Definition: ops.h:223
SwsOpList::order_src
SwsSwizzleOp order_src
Definition: ops.h:227
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:58
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
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
av_strlcat
size_t av_strlcat(char *dst, const char *src, size_t size)
Append the string src to the string dst, but to a total length of no more than size - 1 bytes,...
Definition: avstring.c:95
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:241
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:62
SwsCompiledOp
Definition: ops_internal.h:92
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
SwsReadWriteOp::packed
bool packed
Definition: ops.h:101
ff_sws_pixel_type_name
const char * ff_sws_pixel_type_name(SwsPixelType type)
Definition: ops.c:50
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:49
planes
static const struct @554 planes[]
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_MASK_DIAG3
@ SWS_MASK_DIAG3
Definition: ops.h:172
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:607
SwsReadWriteOp::elems
uint8_t elems
Definition: ops.h:99
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:278
av_add_q
AVRational av_add_q(AVRational b, AVRational c)
Add two rationals.
Definition: rational.c:93
SWS_MASK_DIAG4
@ SWS_MASK_DIAG4
Definition: ops.h:178
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_MASK_MAT4
@ SWS_MASK_MAT4
Definition: ops.h:180
SwsSwizzleOp::in
uint8_t in[4]
Definition: ops.h:127
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:692
av_strlcpy
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:85
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2070
avstring.h
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:222
av_bswap16
#define av_bswap16
Definition: bswap.h:28
SwsOpExec::slice_y
int32_t slice_y
Definition: ops_internal.h:73
SWS_MASK_OFF4
@ SWS_MASK_OFF4
Definition: ops.h:179
SwsContext
Main external API structure.
Definition: swscale.h:191
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